mankee mankee - 6 months ago 55
iOS Question

Programmatically Defined Constraints Swift iOS

I have a

UICollectionView
. I also have the following view which I generate in code:

let messageInputContainerView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.whiteColor()
return view
}()


Now, I want to add the
messageInputContainerView
so that it is attached to the bottom of the screen, and the
UICollectionView
is directly above it. At the moment I have the following constraints :

view.addSubview(messageInputContainerView)
view.addConstraintsWithFormat("H:|[v0]|", views: messageInputContainerView)
view.addConstraintsWithFormat("V:[v0(48)]", views: messageInputContainerView)

bottomConstraint = NSLayoutConstraint(item: messageInputContainerView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraint!)


The problem is that the collectionView is now touching the bottom of the screen as well, and the two views overlap. I tried solving this with the following constraint :

let newConstraint = NSLayoutConstraint(item: messageInputContainerView, attribute: .Top, relatedBy: .Equal, toItem: self.collectionView! , attribute: .Bottom, multiplier: 1, constant: 0)

view.addConstraint(newConstraint)


however this just through a bunch of errors :



016-05-30 19:50:26.153 City[1858:79868] 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:0x7fece04d10e0 h=-&- v=-&- UICollectionView:0x7fece08c0a00.midY == UICollectionViewControllerWrapperView:0x7fece04125f0.midY>",
"<NSAutoresizingMaskLayoutConstraint:0x7fece04d1150 h=-&- v=-&- UICollectionView:0x7fece08c0a00.height == UICollectionViewControllerWrapperView:0x7fece04125f0.height>",
"<NSLayoutConstraint:0x7fece049a460 V:[UIView:0x7fece04992d0(48)]>",
"<NSLayoutConstraint:0x7fece04d7620 UIView:0x7fece04992d0.bottom == UICollectionViewControllerWrapperView:0x7fece04125f0.bottom>",
"<NSLayoutConstraint:0x7fece04a9990 V:[UICollectionView:0x7fece08c0a00]-(0)-[UIView:0x7fece04992d0]>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fece04a9990 V:[UICollectionView:0x7fece08c0a00]-(0)-[UIView:0x7fece04992d0]>

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.




Answer

Please checkout the basic tutorials for autolayout. It will simply give You the needed background for IB and constraints.

The debug log commonly all you need - it tells You the exact info for your problem. Let's think about it in detail

NSLayoutConstraint:0x7fece04a9990 V:[UICollectionView:0x7fece08c0a00]-(0)-[UIView:0x7fece04992d0]

UICollectionView:0x7fece08c0a00 - it's your collection view, it's simple UIView:0x7fece04992d0 - it's your messageInputContainerView - that's clear from the following constraint:

"<NSLayoutConstraint:0x7fece049a460 V:[UIView:0x7fece04992d0(48)]>"

which generated by this line of code:

view.addConstraintsWithFormat("V:[v0(48)]", views: messageInputContainerView)

So, if you can see - uicollection view has NSAutoresizingMaskLayoutConstraint - so, those one were generated by default. You have two ways - set up your collection view constraints via IB - set leading, trainling, top (to whatever value you need) and bottom constraint with the value 48.

The second one - remove autogenerated constraint programmatically and add new one, as following:

[collectionView removeConstraints: [collectionView constraints]]; 
        // top constraint
    view.addConstraint(NSLayoutConstraint(item: self.collectionView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0))
    // leading constraint
    view.addConstraint(NSLayoutConstraint(item: self.collectionView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0))
    // trailing constaint
    view.addConstraint(NSLayoutConstraint(item: self.collectionView, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0))
    // your constraint
    let newConstraint = NSLayoutConstraint(item: messageInputContainerView, attribute: .Top, relatedBy: .Equal, toItem: self.collectionView! , attribute: .Bottom, multiplier: 1, constant: 0)

    view.addConstraint(newConstraint)

Hope this helps.