質問

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?

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>
役に立ちましたか?

解決

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.

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.

他のヒント

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.

The takeaway: make sure you are adding/manipulating your supplementary/header/footer views after the tableview has been sized by AutoLayout.

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:

view.translatesAutoresizingMaskIntoConstraints = false

After banging my head for a while i found this link. In my case it was happening on the UITableViewHeaderFooterView when i was using insertRows or deleteRows from my UIVieController. 'estimatedSectionHeaderHeight' and 'estimatedRowHeight' where set, my constraints redone 3 times ... The error shown was:

"<NSLayoutConstraint:0x280648140 H:|-(8)-[UIImageView:0x106e94860]   (active, names: '|':DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50 )>",
"<NSLayoutConstraint:0x280648230 H:[UIImageView:0x106e94860]-(8)-[DAT_Air_Vinyl.MainLabel:0x106ea5750'The Coral - The Invisible...']   (active)>",
"<NSLayoutConstraint:0x280648410 H:[UIButton:0x106ea5a40]-(8)-|   (active, names: '|':DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50 )>",
"<NSLayoutConstraint:0x280648460 H:[DAT_Air_Vinyl.MainLabel:0x106ea5750'The Coral - The Invisible...']-(8)-[UIButton:0x106ea5a40]   (active)>",
"<NSLayoutConstraint:0x2806493b0 'UIView-Encapsulated-Layout-Width' DAT_Air_Vinyl.ExportsAlbumTableViewHeader:0x106e8fb50.width == 0   (active)>"

As stated on the link:

"When you do insertRows or deleteRows with certain animation types, UIKit will animate the row height from 0 to full height or back. At the 0-end of that animation, the layout equations are impossible to solve if entire vertical axis is set to priority=1000. But lower just one constraint to 999 – say that bottom space to superview margin – and all is fine; the content will just drop-down, outside the cell’s bounds.".

The solution was to set to 999 (or lower to 1000) the leading priority of the UIImageView.

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.

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:

//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 not have to call this from viewDidLayoutSubviews - I was able to use this technique as long as the tableView 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.

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).

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.

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.

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.

I got this error in all sorts of circumstances (not necessarily tied to UICollectionView and friends as suggested by the correct answer here)..

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:

- (void)clearConstraintsOfSubview:(UIView *)subview
{
    for (NSLayoutConstraint *constraint in [self constraints]) {
        if ([[constraint firstItem] isEqual:subview] || [[constraint secondItem] isEqual:subview]) {
            [self removeConstraint:constraint];
        }
    }
}

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?

UIView-Encapsulated-Layout-* are the constraints used by table, collection, and stacks to layout elements as you told them to do it. If you later set constraints that create a change in size you’ll get a conflict. An example of this use case is cells that download images with variable size.

This happens because we are used to layoutIfNeeded() to refresh the layout, but once a collection goes through the rendering cycle the cell size is set with encapsulated constraints. You need to manually invalidate the index path you need to refresh, e.g.

let context = UICollectionViewLayoutInvalidationContext()
context.invalidateItems(at: [cellIndexPath])
collectionView.collectionViewLayout.invalidateLayout(with: context)

this will recursively create more invalidations to shift the positions of other cells and make space.

If you set, e.g. a different height constraint to change the size of the cell it will instantly crash and then recover after you invalidate and the cell is layout again. To avoid this crash you can lower the priority of the height constraint below .required, for instance UILayoutPriority(999).

In my case, I was inadvertently setting up my programmatic constraints twice. As soon as I removed the duplicate call, the conflicts went away.

I catch this problem when I use AL create tableviewHeader

I init tableview like below

let table = UITableView.init(frame: .zero, style: .grouped)
// set table constraint ...

then I create tableviewHeader with AutoLayout.

"<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

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

Finally I found solution for CollectionView! If You using storyBoard, like I am, it will help you!

Interface Builder / Storyboard

Go to storyBoard -> chose your CollectionView ScreenShot CollectionView Go to Size Inspector Then set Estimate Size to None ScreenShot Estimate Size

Thats All!

I was having a similar problem found from testing Split View on the iPad Pro, and DesignatedNerd's answer worked but I didn't need so much code. Here is what I used:

[self.tableView.tableHeaderView setTranslatesAutoresizingMaskIntoConstraints:NO];

NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
                                                                   attribute:NSLayoutAttributeWidth
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:self.tableView
                                                                   attribute:NSLayoutAttributeWidth
                                                                  multiplier:1
                                                                    constant:0];
NSLayoutConstraint *yConstraint = [NSLayoutConstraint constraintWithItem:self.myTableHeaderView
                                                               attribute:NSLayoutAttributeTop
                                                               relatedBy:NSLayoutRelationEqual
                                                                  toItem:self.tableView
                                                               attribute:NSLayoutAttributeTop
                                                              multiplier:1
                                                                constant:0];


[self.tableView addConstraints:@[widthConstraint, yConstraint]];

Note the addition of the Y Constraint, which binds the top of the tableHeaderView to the top of the tableView.

I had the same issue when adding constraints to a Table view header. It seem to occur when adding constraints with set constants when the bounds of the header was (0,0,0,0). I managed to fix this by only adding the constraints in the layout subviews method when the bounds of the header was not (0,0,0,0)

    if self.bounds == CGRect.zero {
        return
    }

constraint UIView-Encapsulated-Layout-Height is created with value you set in tableView.estimatedSectionHeaderHeight

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top