I have a view which I want to look like this:
|---------------|
| | <- navBar
|---------------|
| | <- topView
|---------------|
| |
| |
| |
|---------------|
Everything I want is to stick topView.top to navBar.bottom. I've decided to go with Cartography and implemented following code (trying to stick to MVC ofc):
In my UIViewController
subclass:
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
aView?.topLayoutGuide = self.topLayoutGuide // where aView is my subclass of UIView, inserted in loadView method
}
In my UIView
subclass:
var topLayoutGuide: UILayoutSupport?
override func updateConstraints() {
var constraint = NSLayoutConstraint?()
layout(topView) { (topView) in
constraint = topView.top == topView.superview!.top
}
topView.superview!.removeConstraint(constraint!)
layout(topView) { topView in
topView.top == topView.superview!.top + (self.topLayoutGuide?.length ?? 0)
topView.height == 67
topView.left == topView.superview!.left
topView.width == topView.superview!.width
}
super.updateConstraints()
}
The problem is that I receive following logs and I have no idea how to fix it:
Unable to simultaneously satisfy constraints.
[...]
(
"<NSLayoutConstraint:0x7fe6a64e4800 V:|-(64)-[UIView:0x7fe6a6505d80] (Names: '|':MyApp.MyView:0x7fe6a360d4c0 )>",
"<NSLayoutConstraint:0x7fe6a3538a80 V:|-(0)-[UIView:0x7fe6a6505d80] (Names: '|':MyApp.MyView:0x7fe6a360d4c0 )>"
)
Seems I need some help. How to do it properly? I don't want to implement constraints in UIViewController
and I don't want to use Storyboards
.
Thanks for any help!
One solution: create a reference to that particular NSLayoutConstraint
, and update its constant
:
var topLayoutConstraint: NSLayoutConstraint!
override func updateConstraints() {
layout(topView) { topView in
topLayoutConstraint = topView.top == topView.superview!.top
topView.height == 67
topView.left == topView.superview!.left
topView.width == topView.superview!.width
}
super.updateConstraints()
}
... then later, you can adjust the constant of that NSLayoutConstraint like so:
topLayoutConstraint.constant = 50
The only question that's left is - when do you update the topLayoutConstraint
? One solution is to create a protocol, MyViewInterface
:
protocol MyViewInterface: class {
var topLayoutGuideLength: CGFloat { get set }
}
Then, in our UIView
, when that topLayoutGuideLength
property is changed, we adjust the constant
of our NSLayoutConstraint
:
public class MyView: UIView, MyViewInterface {
public var topLayoutGuideLength: CGFloat = 0 {
didSet {
topLayoutConstraint.constant = topLayoutGuideLength
}
}
}
Finally, in our UIViewController
(where our topLayoutGuide
lives):
public class myViewController: UIViewController {
private var myView: MyView
// ...
public override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
myView.topLayoutGuideLength = topLayoutGuide.length
}
}
The end result? Whenever viewDidLayoutSubviews()
is triggered on our UIViewController
, our myView
's topLayoutGuideLength
property is updated, which triggers a change in that topLayoutConstraint
's constant
.