ios 自动布局:是什么创建了名为 UIView-Encapsulated-Layout-Width & Height 的约束?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23308400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Auto-layout: What creates constraints named UIView-Encapsulated-Layout-Width & Height?
提问by Reuben Scratton
My layout constraints are fine in Interface Builder but an exception occurs at runtime thanks to some part of the framework applying fixed height and width constraints that I really don't want. Why are they there, and how to turn them off?
我的布局约束在 Interface Builder 中很好,但是由于框架的某些部分应用了我真正不想要的固定高度和宽度约束,因此在运行时发生了异常。它们为什么在那里,以及如何关闭它们?
They're the last two constraints shown in the logged list:
它们是记录列表中显示的最后两个约束:
2014-04-26 09:02:58.687 BBCNews[32058:60b] 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)
(
"<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>",
"<NSLayoutConstraint:0xbf47190 UIView:0xbf4a3c0.leading == BNMyNewsCell_landscape:0xbf48b10.leading>",
"<NSLayoutConstraint:0xbf47160 UIView:0xbf4a3c0.trailing == BNMyNewsCell_landscape:0xbf48b10.trailing>",
"<NSLayoutConstraint:0xbf47130 BNMyNewsCell_landscape:0xbf48b10.bottom == UIView:0xbf4a3c0.bottom>",
"<NSLayoutConstraint:0xbf47100 UIView:0xbf4a3c0.top == BNMyNewsCell_landscape:0xbf48b10.top>",
"<NSLayoutConstraint:0xd4c3c40 'UIView-Encapsulated-Layout-Width' H:[BNMyNewsCell_landscape:0xbf48b10(304)]>",
"<NSLayoutConstraint:0xd4c38a0 'UIView-Encapsulated-Layout-Height' V:[BNMyNewsCell_landscape:0xbf48b10(290)]>"
}
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0xbf478a0 UIView:0xbf4a3c0.height == 0.28125*UIView:0xbf4a3c0.width>
回答by Reuben Scratton
Based on a ton of observation I believe (but cannot know for certain) that the constraints named UIView-Encapsulated-Layout-Width
and UIView-Encapsulated-Layout-Height
are created by UICollectionView
and friends, and exist to enforce the size returned by the sizeForItemAtIndexPath
delegate method. I guess it's there to ensure that the UICollectionViewCell
set up by cellForItemAtIndexPath
ends up the size that it was told it would be.
基于大量的观察,我相信(但不能确定)约束命名UIView-Encapsulated-Layout-Width
和UIView-Encapsulated-Layout-Height
由UICollectionView
朋友创建,并且存在以强制执行sizeForItemAtIndexPath
委托方法返回的大小。我想它的存在是为了确保UICollectionViewCell
设置cellForItemAtIndexPath
最终达到它被告知的大小。
Which answers my initial question here. A second question is why were the constraints unsatisfiable? The cell's intrinsic height should have been the same as UIView-Encapsulated-Layout-Height
. Again, I don't know for certain, but I suspect it was a rounding error (i.e. intrinsic height came to 200.1 pixels, the UIView-Encapsulated-Layout-Height
maybe rounded to 200. The fix I came up with was to just lower the priority of the relevant cell constraint to allow UIView-Encapsulated-Layout-Height
to have the last word.
这在这里回答了我最初的问题。第二个问题是为什么约束不可满足?单元格的固有高度应该与 相同UIView-Encapsulated-Layout-Height
。同样,我不确定,但我怀疑这是一个舍入错误(即固有高度达到 200.1 像素,UIView-Encapsulated-Layout-Height
可能舍入为 200。我想出的解决方法是降低相关单元格约束的优先级允许UIView-Encapsulated-Layout-Height
有最后的发言权。
回答by Yerk
This may not answer your question, but it could help others like me who got here from search.
这可能无法回答您的问题,但它可以帮助像我这样从搜索到这里的其他人。
I was getting a strange AutoLayout broken constraint error accompanied by a UIView-Encapsulated-Layout-Width
constraint because I was adding a tableHeaderView
to a table view that hadn't been sized with AutoLayout yet. So the system was trying to apply my header subviews' constraints inside a tableview with a frame of {0,0,0,0}
. Since UITableView likes control over the width of its elements, its generated width constraint, UIView-Encapsulated-Layout-Width
, was set to zero, causing all kinds of confusion with my header elements that were expecting 320+pt width.
我收到一个奇怪的 AutoLayout 破坏约束错误并伴随着一个UIView-Encapsulated-Layout-Width
约束,因为我正在向一个tableHeaderView
尚未使用 AutoLayout 调整大小的表视图添加一个。因此,系统试图将我的标题子视图的约束应用于框架为{0,0,0,0}
. 由于 UITableView 喜欢控制其元素的宽度,因此它生成的宽度约束 ,UIView-Encapsulated-Layout-Width
被设置为零,导致与我期望 320+pt 宽度的标题元素的各种混淆。
The takeaway: make sure you are adding/manipulating your supplementary/header/footer views after the tableview has been sized by AutoLayout.
要点:确保在 AutoLayout 调整 tableview 大小后添加/操作您的补充/页眉/页脚视图。
回答by Zoltán
I was facing the same weird constraint and had no idea why, until I remembered the darned translatesAutoresizingMaskIntoConstraints
property. Setting this to false
solved the problem.
What happens in the background is that the auto resizing masks (the old layout engine for iOS) are converted to constraints. Very often you don't want these constraints and want your own ones. In such cases you should set this property to false and you'll be fine:
我面临着同样奇怪的约束,不知道为什么,直到我想起了该死的translatesAutoresizingMaskIntoConstraints
财产。设置这个来false
解决问题。在后台发生的事情是自动调整大小蒙版(iOS 的旧布局引擎)被转换为约束。很多时候你不想要这些约束,而是想要你自己的约束。在这种情况下,您应该将此属性设置为 false ,您会没事的:
view.translatesAutoresizingMaskIntoConstraints = false
回答by tyler
We've started seeing tons of layout conflicts in iOS 11 that include references to these constraints and they are in fact added via the translatesAutoresizingMaskIntoConstraints
flag. It seems that in iOS 11 there's a lot more AutoLayout magic happening when a view is added to the hierarchy rather than just when the view is laid out (as it seemed to work in previous iOS versions).
我们已经开始在 iOS 11 中看到大量布局冲突,其中包括对这些约束的引用,实际上它们是通过translatesAutoresizingMaskIntoConstraints
标志添加的。似乎在 iOS 11 中,当将视图添加到层次结构而不是仅在布局视图时(因为它似乎在以前的 iOS 版本中工作),会发生更多的 AutoLayout 魔法。
This is the case that we were running into:
这是我们遇到的情况:
- Create a view whose internal layout helps define the views size (e.g., the view has internal constraints that includes explicit padding, etc.)
- *** Add this view to the hierarchy.
- Set the translatesAutoresizingMaskIntoConstraints to false some time later, before the layout pass.
- 创建一个视图,其内部布局有助于定义视图大小(例如,视图具有内部约束,包括显式填充等)
- *** 将此视图添加到层次结构中。
- 稍后,在布局传递之前,将 translatesAutoresizingMaskIntoConstraints 设置为 false。
The second step (***) will result in a conflict because the system will add zero size constraints to the view at the time the view is added to the hierarchy. We were setting translatesAutoresizingMaskIntoConstraints
later as a result of using the PureLayout framework which automatically sets this flag correctly when you constrain the view... That said, in iOS 11 you need to remember to turn off translatesAutoresizingMaskIntoConstraints
at construction time, before the view is added to the hierarchy.
第二步(***)会导致冲突,因为在将视图添加到层次结构时,系统会向视图添加零大小约束。我们translatesAutoresizingMaskIntoConstraints
稍后设置是因为使用 PureLayout 框架,当您约束视图时,该框架会自动正确设置此标志......也就是说,在 iOS 11 中,您需要记住translatesAutoresizingMaskIntoConstraints
在构建时关闭视图,然后再将视图添加到等级制度。
I suspect Apple thought that defaulting this flag to YES would be way more helpful than it is painful. Unfortunately, this has not been the case.
我怀疑 Apple 认为将此标志默认为 YES 会比痛苦更有帮助。不幸的是,情况并非如此。
回答by abbood
I got this error in all sorts of circumstances (not necessarily tied to UICollectionView and friends as suggested by the correctanswer here)..
我在各种情况下都遇到了这个错误(不一定与此处正确答案所建议的 UICollectionView 和朋友有关)。
So my way of dealing with it was simply clearing all the constraints then building them again (only this time i have no fear of my constraints colliding with these pre-created ones):
所以我处理它的方法只是清除所有约束然后再次构建它们(只有这一次我不担心我的约束与这些预先创建的约束冲突):
so in code:
所以在代码中:
UIView *parentView = [viewInQuestion superview];
[parentView clearConstraintsOfSubview:viewInQuestion];
where clearConstraintsOfSubview
is a category method on UIView:
clearConstraintsOfSubview
UIView 上的类别方法在哪里:
- (void)clearConstraintsOfSubview:(UIView *)subview
{
for (NSLayoutConstraint *constraint in [self constraints]) {
if ([[constraint firstItem] isEqual:subview] || [[constraint secondItem] isEqual:subview]) {
[self removeConstraint:constraint];
}
}
}
回答by DesignatedNerd
Definitely seeing this on a UITableView
's tableHeaderView
. I was able to get this to work with a custom header view by explicitly setting the width equal to that of the tableView
after setting the tableHeaderView
, THEN resetting it after a layout pass has completed.
在清楚看到这个UITableView
的tableHeaderView
。我可以通过显式设置宽度得到这个带有自定义标题视图的工作等于的tableView
设置后tableHeaderView
,重新那么它的布局传递完成之后。
Example code for iOS 9, which assumes you have a UITableView
passed into your method as tableView
and an item to configure it as item
:
iOS 9 的示例代码,假设您已将UITableView
as 传递到方法中,tableView
并将其配置为item
:
//Create the header view
self.contentDetailHeaderView = MyCustomHeaderView()
//Turn on autolayout
self.contentDetailHeaderView.translatesAutoresizingMaskIntoConstraints = false
//Add the header to the table view
tableView.tableHeaderView = self.contentDetailHeaderView
//Pin the width
let widthConstraint = NSLayoutConstraint(item: self.contentDetailHeaderView,
attribute: .Width,
relatedBy: .Equal,
toItem: tableView,
attribute: .Width,
multiplier: 1,
constant: 0)
tableView.addConstraint(widthConstraint)
//Do whatever configuration you need to - this is just a convenience method I wrote on my header view.
self.contentDetailHeaderView.setupForItem(item)
//Lay out the configured view
self.contentDetailHeaderView.layoutIfNeeded()
//Reset the table header view, because ˉ\_(ツ)_/ˉ
tableView.tableHeaderView = self.contentDetailHeaderView
Couple of notes, mostly for when I look this up again because I have the memory of a goldfish:
几个注意事项,主要是当我再次查找时,因为我有一条金鱼的记忆:
- You do nothave to call this from
viewDidLayoutSubviews
- I was able to use this technique as long as thetableView
has the appropriate width during setup. - You do need to make sure your header view is set up to automatically resize itself. I did this by creating a
.xib
and then making sure all items were pinned so that as the view changed width, the height would then update. - If you're trying to do this for
viewForHeaderInSection
, you're probably better off grabbing something offscreen which you can lay out a la this technique. I haven't had much luck with the self-sizing bits.
- 你不必须从调用这个
viewDidLayoutSubviews
-我能够使用这种技术,只要tableView
有安装过程中适当的宽度。 - 您确实需要确保您的标题视图设置为自动调整大小。我通过创建一个
.xib
然后确保所有项目都被固定来做到这一点,这样当视图改变宽度时,高度就会更新。 - 如果你想这样做
viewForHeaderInSection
,你可能最好在屏幕外抓一些东西,你可以用这种技术来布置。我对自行调整大小的位没有多少运气。
回答by Ben Oveson
I was facing a similar issue and solved it with the following.
我遇到了类似的问题,并通过以下方法解决了它。
Environment:Swift 5.0, xcode 10.2.1, Setting views programmatically
Warning message:Unable to simultaneously satisfy constraints... 'UIView-Encapsulated-Layout-Width' UIView:0x0000000000.width == 0 (active)>" )
Code with warning
override func loadView() { view = UIView() /// Adds the subviews to the view and sets their properties and constraints setupViews() }
Code that cleared warning
override func loadView() { /// Needed to set the frame of the root view to the window frame. let window = UIWindow() view = UIView(frame: window.frame) /// Adds the subviews to the view and sets their properties and constraints setupViews() }
Notes on the loadView() method:"If you use Interface Builder to create your views and initialize the view controller, you must not override this method. You can override this method in order to create your views manually. If you choose to do so, assign the root view of your view hierarchy to the view property. The views you create should be unique instances and should not be shared with any other view controller object. Your custom implementation of this method should not call super." - Apple documentation
Notes on the root view:
"If you prefer to create views programmatically ... you do so by overriding your view controller's loadView method. Your implementation of this method should do the following:
Create a root view object. The root view contains all other views associated with your view controller. You typically define the frame for this view to match the size of the app window, which itself should fill the screen. However, the frame is adjusted based on how your view controller is displayed. See “View Controller View Resizing.”
You can use a generic UIView object, a custom view you define, or any other view that can scale to fill the screen.
Create additional subviews and add them to the root view." - Old apple documentation?
环境:Swift 5.0,xcode 10.2.1,以编程方式设置视图
警告消息:无法同时满足约束... 'UIView-Encapsulated-Layout-Width' UIView:0x0000000000.width == 0 (active)>" )
带警告的代码
override func loadView() { view = UIView() /// Adds the subviews to the view and sets their properties and constraints setupViews() }
清除警告的代码
override func loadView() { /// Needed to set the frame of the root view to the window frame. let window = UIWindow() view = UIView(frame: window.frame) /// Adds the subviews to the view and sets their properties and constraints setupViews() }
关于 loadView() 方法的注意事项:“如果您使用 Interface Builder 创建视图并初始化视图控制器,则不得覆盖此方法。您可以覆盖此方法以手动创建视图。如果您选择这样做,将视图层次结构的根视图分配给视图属性。您创建的视图应该是唯一的实例,不应与任何其他视图控制器对象共享。此方法的自定义实现不应调用 super。” - 苹果文档
关于根视图的注意事项:
“如果您更喜欢以编程方式创建视图……您可以通过覆盖视图控制器的 loadView 方法来实现。此方法的实现应执行以下操作:
创建一个根视图对象。根视图包含与您的视图控制器关联的所有其他视图。您通常定义此视图的框架以匹配应用程序窗口的大小,应用程序窗口本身应填满屏幕。但是,框架会根据您的视图控制器的显示方式进行调整。请参阅“视图控制器视图调整大小”。
您可以使用通用 UIView 对象、您定义的自定义视图或任何其他可以缩放以填充屏幕的视图。
创建其他子视图并将它们添加到根视图中。” - 旧的苹果文档?
回答by Aleksandr Skorotkin
Finally I found solution for CollectionView! If You using storyBoard, like I am, it will help you!
最后我找到了 CollectionView 的解决方案!如果您像我一样使用 storyBoard,它将对您有所帮助!
Interface Builder / Storyboard
界面生成器/故事板
Go to storyBoard-> chose your CollectionViewScreenShot CollectionViewGo to Size Inspector Then set Estimate Sizeto NoneScreenShot Estimate Size
转到storyBoard-> 选择你的CollectionViewScreenShot CollectionView转到 Size Inspector 然后将Estimate Size设置为NoneScreenShot Estimate Size
Thats All!
就这样!
回答by tcarey
In my case, I was inadvertently setting up my programmatic constraints twice. As soon as I removed the duplicate call, the conflicts went away.
就我而言,我无意中设置了两次编程约束。一旦我删除了重复的调用,冲突就消失了。
回答by kkklc
I catch this problem when I use AL create tableviewHeader
我在使用 AL create tableviewHeader 时遇到了这个问题
I init tableview like below
我像下面这样初始化 tableview
let table = UITableView.init(frame: .zero, style: .grouped)
// set table constraint ...
then I create tableviewHeader with AutoLayout.
然后我用 AutoLayout 创建 tableviewHeader。
"<NSLayoutConstraint:0x600003d7d130 'UIView-Encapsulated-Layout-Width' UIView:0x7fe92bc55260.width == 0 (active)>"
"<NSLayoutConstraint:0x600003d7d130 'UIView-Encapsulated-Layout-Width' UIView:0x7fe92bc55260.width == 0 (active)>"
symbolic breakpoint appear
出现符号断点
After I refer @Yerk 's the answer. I change the frame when I init tableView
在我提到@Yerk 之后,答案是。我在初始化 tableView 时更改了框架
let rect = CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 0)
let table = UITableView.init(frame:rect , style: .grouped)
The problem seems to be solved
问题好像解决了