Sen*_*ful 6 ios autolayout uicollectionview uicollectionviewcell
TL; DR
尝试通过自动布局调整UICollectionViewCells的大小时,即使是一个简单的示例,您也可以轻松获得自动布局警告.
我们应该contentView.translatesAutoResizingMaskToConstraints = false摆脱它们吗?
我正在尝试使用自定义自动布局单元格创建UICollectionView .
在viewDidLoad中:
let layout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 10, height: 10)
collectionView.collectionViewLayout = layout
Run Code Online (Sandbox Code Playgroud)
我的细胞很基础.它是一个宽度和高度为75的蓝色视图,用于测试目的.通过将视图固定到所有4个边上的超视图,并为其指定高度和宽度来创建约束.
class MyCell: UICollectionViewCell {
override init(frame: CGRect) {
view = UIView()
super.init(frame: frame)
view.backgroundColor = UIColor.blueColor()
contentView.addSubview(view)
installConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var view: UIView
func installConstraints() {
view.translatesAutoresizingMaskIntoConstraints = false
var c: NSLayoutConstraint
// pin all edges
c = NSLayoutConstraint(item: contentView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0)
c.active = true
c = NSLayoutConstraint(item: contentView, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0)
c.active = true
c = NSLayoutConstraint(item: contentView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0)
c.active = true
c = NSLayoutConstraint(item: contentView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0)
c.active = true
// set width and height
c = NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 75)
c.active = true
c = NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 75)
c.active = true
}
}
Run Code Online (Sandbox Code Playgroud)
运行代码时,我得到两个关于无法同时满足约束的错误:
Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
"<NSAutoresizingMaskLayoutConstraint:0x7fed88c1bac0 h=--& v=--& H:[UIView:0x7fed8a90c2f0(10)]>",
"<NSLayoutConstraint:0x7fed8ab770b0 H:[UIView:0x7fed8a90bbe0(75)]>",
"<NSLayoutConstraint:0x7fed8a90d610 UIView:0x7fed8a90c2f0.leading == UIView:0x7fed8a90bbe0.leading>",
"<NSLayoutConstraint:0x7fed8ab005f0 H:[UIView:0x7fed8a90bbe0]-(0)-| (Names: '|':UIView:0x7fed8a90c2f0 )>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fed8ab770b0 H:[UIView:0x7fed8a90bbe0(75)]>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
Run Code Online (Sandbox Code Playgroud)
(省略了关于垂直约束的第二个错误,但它几乎相同.)
这些错误是有道理的.本质上,contentView(0x7fed8a90c2f0)具有translatesAutoresizingMask = true,因此其边界设置为10x10(从估计的大小)创建该NSAutoresizingMaskLayoutConstraint约束.视图无法同时为10像素宽和75像素宽,因此会输出错误.
我尝试了几种不同的方法来解决这个问题,其中只有2个似乎有效.
在这种情况下,如果我更改将蓝色视图底部固定到contentView底部的约束为999优先级(并对尾随约束执行相同操作),则可以解决问题.同样,我可以选择Top + Leading,或Width + Height,只要我在每个方向都没有1000个优先级.至少有一个必须"给"初始布局.
虽然听起来可能会导致视图的高度/宽度不正确,但实际上看起来大小正确.
contentView.translatesAutoresizingMaskIntoConstraints = false通过设置它false,它基本上摆脱了NSAutoresizingMaskLayoutConstraint约束,我们不再有冲突.另一方面,由于contentView没有设置为自动布局,所以无论你如何改变它的框架,它的超级视图(单元格)永远不会更新它的大小,因为没有"推"反对它.
尽管如此,这种解决方案仍然神奇地起作用.我假设在某些时候,单元格会查看contentView的框架,并决定"嘿,你把我设置为这个大小,这可能意味着你希望单元格也是这个尺寸",并负责为你调整单元格的大小.
这个问题有更好的解决方案吗?应该使用以上哪个?上面提到的两个解决方案是否存在任何缺点,或者它们基本相同,并且您使用哪个解决方案并不重要?
笔记:
width@1000行为与 intrinsicWidth + 不同contentCompressionResistance@1000.也许视图获得其固有宽度的时间是可以修改内容视图的框架之后.没用的东西:
view通过故事板创建的单元格和以编程方式创建的单元格的差异时,我注意到故事板中的autoresizingMaskRM + BM(36),但是通过代码创建时为0.我尝试手动将autoresizingMask我的蓝色视图设置为36,但这不起作用.故事板解决方案的真正原因在于,您创建的约束通常已在故事板中完美设置(否则您将需要修复故事板错误).作为修复这些错误的一部分,您可以更改单元格的边界.由于它调用init?(coder:),已经正确配置了单元格的边界.因此,即使contentView.autoresizingMask = true它没有冲突,因为它已经以正确的大小定义.updateConstraints.然而,听起来这是Apple的原始建议,并且他们不再为大多数约束推荐此约定.尽管如此,因为那篇文章说要创建它们updateConstraints我试图无济于事.我得到了相同的结果.这是因为在调用updateConstraints时它没有更新帧(它仍然是10x10).非解决方案:
collectionView(_:layout:sizeForItemAtIndexPath:)(例如通过创建尺寸单元).我想要一个直接适用于我的手机的解决方案,无需任何记账或额外计算.经过测试后,我注意到至少有一个保留contentView.translatesAutoresizingMaskToConstraints = true.
如果您用来preferredLayoutAttributesFittingAttributes(_:)更改 a 的尺寸UICollectionViewCell,它可以通过将 contentView 的大小调整为正确的尺寸来工作。如果您设置contentView.translatesAutoresizingMaskToConstraints = false,您将失去此功能。
因此,我建议使用解决方案 1(将每个维度中的至少一个约束更改为不需要)。事实上,我为 UICollectionViewCell 创建了一个包装器,它将处理所需的 999 约束,以及一种获得首选高度或宽度以正确运行的方法。
通过使用此包装器,您无需记住使 UICollectionViewCell 中的 contentView 正常运行的复杂性。
class CollectionViewCell<T where T: UIView>: UICollectionViewCell {
override init(frame: CGRect) {
preferredHeight = nil
preferredWidth = nil
super.init(frame: frame)
}
var preferredWidth: CGFloat?
var preferredHeight: CGFloat?
private(set) var view: T?
func initializeView(view: T) {
assert(self.view == nil)
self.view = view
contentView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
var constraint: NSLayoutConstraint
constraint = NSLayoutConstraint(item: view, attribute: .Top, relatedBy: .Equal, toItem: contentView, attribute: .Top, multiplier: 1, constant: 0)
constraint.active = true
constraint = NSLayoutConstraint(item: view, attribute: .Leading, relatedBy: .Equal, toItem: contentView, attribute: .Leading, multiplier: 1, constant: 0)
constraint.active = true
// Priority must be less than 1000 to prevent errors when installing
// constraints in conjunction with the contentView's autoresizing constraints.
let NonRequiredPriority: UILayoutPriority = UILayoutPriorityRequired - 1
constraint = NSLayoutConstraint(item: view, attribute: .Bottom, relatedBy: .Equal, toItem: contentView, attribute: .Bottom, multiplier: 1, constant: 0)
constraint.priority = NonRequiredPriority
constraint.active = true
constraint = NSLayoutConstraint(item: view, attribute: .Trailing, relatedBy: .Equal, toItem: contentView, attribute: .Trailing, multiplier: 1, constant: 0)
constraint.priority = NonRequiredPriority
constraint.active = true
}
override func preferredLayoutAttributesFittingAttributes(layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
let newLayoutAttributes = super.preferredLayoutAttributesFittingAttributes(layoutAttributes)
if let preferredHeight = preferredHeight {
newLayoutAttributes.bounds.size.height = preferredHeight
}
if let preferredWidth = preferredWidth {
newLayoutAttributes.bounds.size.width = preferredWidth
}
return newLayoutAttributes
}
}
Run Code Online (Sandbox Code Playgroud)
(注意:由于UICollectionViewCell 的通用子类存在错误,因此需要 init 方法。)
注册:
collectionView.registerClass(CollectionViewCell<UIView>.self, forCellWithReuseIdentifier: "Cell")
Run Code Online (Sandbox Code Playgroud)
使用方法:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! CollectionViewCell<UILabel>
if cell.view == nil {
cell.initializeView(UILabel())
}
cell.view!.text = "Content"
cell.preferredHeight = collectionView.bounds.height
return cell
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1686 次 |
| 最近记录: |