使用“自动布局”指定动态通知中心窗口小部件高度

时间:2015-03-02 07:25:56

标签: ios autolayout ios-app-extension ios8-today-widget

我的通知中心窗口小部件需要具有基于其包含的内容的动态高度。我有一个简单的界面 - 一个UILabel下面有一个UICollectionView。 (集合视图将根据我为流布局中的单元格提供的大小而增加高度。)

为了正确指定小部件的高度,需要哪些约束?

我认为在标签上提供一个Top约束以将其固定到视图顶部就足够了,指定集合视图顶部与标签的底部对齐,然后提供集合视图的固定高度(在itemSize更改时更新),然后将集合视图的底部对齐到视图的底部。但这会导致两个破坏的约束 - 集合视图的高度以及标签和集合视图之间的垂直约束。

let label = //...
label.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(label)
self.view.addConstraint(NSLayoutConstraint(item: label, attribute: .Top, relatedBy: .Equal, toItem: self.view, attribute: .Top, multiplier: 1, constant: 10))
self.view.addConstraint(NSLayoutConstraint(item: label, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 1, constant: 25))
self.view.addConstraint(NSLayoutConstraint(item: label, attribute: .Leading, relatedBy: .Equal, toItem: self.view, attribute: .LeadingMargin, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: label, attribute: .Trailing, relatedBy: .Equal, toItem: self.view, attribute: .TrailingMargin, multiplier: 1, constant: 0))

let collectionView = //...
collectionView.setTranslatesAutoresizingMaskIntoConstraints(false)
self.view.addSubview(collectionView)
self.view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: .Leading, relatedBy: .Equal, toItem: label, attribute: .Leading, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: .Trailing, relatedBy: .Equal, toItem: label, attribute: .Trailing, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: .Top, relatedBy: .Equal, toItem: label, attribute: .Bottom, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: collectionView, attribute: .Bottom, relatedBy: .Equal, toItem: self.view, attribute: .Bottom, multiplier: 1, constant: 0))
self.collectionViewHeightConstraint = NSLayoutConstraint(item: collectionView, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .Height, multiplier: 1, constant: 100)
collectionView.addConstraint(self.collectionViewHeightConstraint)

//later on
let flowLayout = collectionView.collectionViewLayout as UICollectionViewFlowLayout
flowLayout.itemSize = //some new size
self.collectionViewHeightConstraint.constant = flowLayout.itemSize.height * numberOfRows

问题:

Unable to simultaneously satisfy constraints.
"<NSLayoutConstraint:0x6080000998c0 V:|-(10)-[UILabel:0x6000001db300]   (Names: '|':UIView:0x60800018f700 )>",
"<NSLayoutConstraint:0x608000099e60 V:[UILabel:0x6000001db300(25)]>",
"<NSLayoutConstraint:0x60800009a2c0 V:[UILabel:0x6000001db300]-(0)-[UICollectionView:0x7ff94b02c200]>",
"<NSLayoutConstraint:0x60800009a310 UICollectionView:0x7ff94b02c200.bottom == UIView:0x60800018f700.bottom>",
"<NSLayoutConstraint:0x60800009a360 V:[UICollectionView:0x7ff94b02c200(100)]>",
"<NSLayoutConstraint:0x60800009a4f0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x60800018f700(667)]>"
Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x60800009a360 V:[UICollectionView:0x7ff94b02c200(100)]>

Unable to simultaneously satisfy constraints.
"<NSLayoutConstraint:0x6080000998c0 V:|-(10)-[UILabel:0x6000001db300]   (Names: '|':UIView:0x60800018f700 )>",
"<NSLayoutConstraint:0x608000099e60 V:[UILabel:0x6000001db300(25)]>",
"<NSLayoutConstraint:0x60800009a2c0 V:[UILabel:0x6000001db300]-(0)-[UICollectionView:0x7ff94b02c200]>",
"<NSLayoutConstraint:0x60800009a310 UICollectionView:0x7ff94b02c200.bottom == UIView:0x60800018f700.bottom>",
"<NSLayoutConstraint:0x60800009a4f0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x60800018f700(0)]>"
Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x60800009a2c0 V:[UILabel:0x6000001db300]-(0)-[UICollectionView:0x7ff94b02c200]>

2 个答案:

答案 0 :(得分:1)

在第一组冲突约束中,问题实际上是您的视图最终比通知中心似乎想要更短。以下是约束,稍微重新排序,以便从上到下阅读:

<NSLayoutConstraint:0x6080000998c0 V:|-(10)-[UILabel:0x6000001db300]   (Names: '|':UIView:0x60800018f700 )>
<NSLayoutConstraint:0x608000099e60 V:[UILabel:0x6000001db300(25)]>
<NSLayoutConstraint:0x60800009a2c0 V:[UILabel:0x6000001db300]-(0)-[UICollectionView:0x7ff94b02c200]>
<NSLayoutConstraint:0x60800009a360 V:[UICollectionView:0x7ff94b02c200(100)]>
<NSLayoutConstraint:0x60800009a310 UICollectionView:0x7ff94b02c200.bottom == UIView:0x60800018f700.bottom>

因此,容器视图(UIView:0x60800018f700)需要10 + 25 + 100 == 135点高。这与通知中心施加的这种限制相冲突:

<NSLayoutConstraint:0x60800009a4f0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x60800018f700(667)]>

决定了667点的高度。

通知中心希望容器视图高于您允许的容器视图。您可以在集合视图的底部和其超级视图的底部之间更改约束,使其成为不等式 - 超级视图的底部大于或等于集合视图的底部。或者,您可以将集合视图高度上的约束更改为不等式 - 高度大于或等于100。

然而,第二种情况是相反的问题。在这种情况下,通知中心在容器视图上强加了0的高度。您的约束要求高度不为零,因此这是一个冲突。你的约束,稍微重新排序:

<NSLayoutConstraint:0x6080000998c0 V:|-(10)-[UILabel:0x6000001db300]   (Names: '|':UIView:0x60800018f700 )>
<NSLayoutConstraint:0x608000099e60 V:[UILabel:0x6000001db300(25)]>
<NSLayoutConstraint:0x60800009a2c0 V:[UILabel:0x6000001db300]-(0)-[UICollectionView:0x7ff94b02c200]>
<NSLayoutConstraint:0x60800009a310 UICollectionView:0x7ff94b02c200.bottom == UIView:0x60800018f700.bottom>

容器必须是10 + 25 +高度的集合视图(隐含地> = 0)&gt; = 35点高。通知中心的约束:

<NSLayoutConstraint:0x60800009a4f0 'UIView-Encapsulated-Layout-Height' V:[UIView:0x60800018f700(0)]>

我不清楚为什么通知中心会像这样改变强加的高度。这似乎是在忽略集合视图高度解决第一个冲突后发生的,因为在此日志中省略了这一点。

尝试修复第一个并查看第二个是否消失。如果没有,你可以将集合视图底部和超级视图底部之间约束的优先级降低到900.这仍然几乎是必需的,所以应该尽可能保持你的布局,但它不是实际需要的时间更长 - 它是可选的 - 所以它不能导致不可满足的约束。

答案 1 :(得分:0)

self.view.addConstraint(self.collectionViewHeightConstraint)更改为collectionView.addConstraint(self.collectionViewHeightConstraint),因为此约束适用于collectionView,而不是其superView。