Can constraints in Xcode 6 be dynamic?

2019-08-01 23:11发布

问题:

I'm biting the bullet and have begun using auto layout. Not as difficult to get used to than I feared. I am having trouble with this though.

Is it possible for view 2 to 'anchor' to the bottom of view 1.. UNLESS view 1 is hidden, in which case it should anchor to the top of the containing view? How would I set constraints for this?

If View1.hidden = NO:

If View1.hidden = YES:

回答1:

Perhaps the easiest way to solve this issue is to manipulate constraint priorities.

I won't go into detail regarding how the Auto Layout system works – if you need that, check out WWDC sessions 202: Introduction to Auto Layout for iOS and OS X and 228: Best Practices for Mastering Auto Layout, both from 2012. Also, check out objc.io's great article on Auto Layout.

In short, constraints can be assigned priorities. A priority is represented by a floating point number in the range [0, 1000], inclusive. The special priority of 1000 means "required;" all other priorities are optional.


How can we use this to help? In Interface Builder (IB), create two different constraints for the top of your blue view. The first one should relate the top of the blue view to the bottom of the red view, with a distance of 0 points – that is, the top of the blue view should be flush with the bottom of the red view. We'll call this constraint the "unhidden" constraint.

The second constraint should go from the top of the blue view to the top of the superview, again with a distance of 0 points – that is, the top of the blue view should be flush with the top of its superview. We'll call this constraint the "hidden" constraint.

If you're following along, you've realized that it is impossible to satisfy both of these constraints simultaneously unless the red view's height is exactly equal to 0. That won't happen, though (I assume you're not resizing the red view, just hiding or unhiding it), so how can these constraints coexist? The answer lies in priorities. Make sure that, in IB, the priority of the unhidden constraint is 950 – remember, this optional, but at the high priority of 950. However, set the priority of the hidden constraint to something less than 950 – maybe 450, for example.

Next, you'll need to create outlets to these two constraints (how to do so is outside the scope of this answer – it's the same as creating outlets to anything in IB). I recommend naming them as I've named them here. So, in your header file, you might see the following:

...
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *unhiddenConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *hiddenConstraint;
...

Now the only thing left is to alter the priorities of these constraints at the appropriate times – namely, when you hide or unhide the red view. So, in your view controller, add a method like this:

- (void)setRedViewHidden:(BOOL)hidden {
    if (hidden) {
        self.unhiddenConstraint.priority = 450;
        self.hiddenConstraint.priority   = 950;
        self.redView.hidden = YES;
    } else {
        self.hiddenConstraint.priority   = 450;
        self.unhiddenConstraint.priority = 950;
        self.redView.hidden = NO;
    }
}

Then, whenever you want to hide the red view, you just call this method with the proper argument. Setting the priorities on the constraints will implicitly mark the layout as dirty, causing a new layout pass to occur on the next turn of the UI loop.

This is not a beginner use of the Auto Layout engine. Let me know if you have any lingering questions about this solution.