5
votes

Is it possible to apply such auto layout constraints with aspect ratio calculated on the fly based on intrinsicContentSize? In the documentation I've found only constrains with fixed ratio value.

Actual with and height from intrinsicContentSize is not important in my use case, I want preserve height and width ratio of the view which changes dynamically.

Should I provide my own implementation of the constraint? Or is there a better way?

3

3 Answers

1
votes

The intrinsicContentSize is not available as input to constraints. (Logically, there are constraints that implement the intrinsicContentSize and related content-hugging and compression-resistance priorities, but that's different.)

If you want such an aspect ratio constraint, you'll have to add it yourself. It's easy enough to query the intrinsicContentSize at a given moment, verify that it provides real values (not NSViewNoInstrinsicMetric) for both dimensions, compute the aspect ratio, and then use that as the multiplier in a constraint that relates the item's width to its height.

The hard part is knowing when the intrinsicContentSize has been invalidated so you can remove the old aspect ratio constraint and add a new one. You can do that as a subclass of the view by overriding -invalidateIntrinsicContentSize. (Be sure to call through to super!) However, I don't know of a way to do that from a controller or superview.

0
votes

You can find the answer on how to set up ratio-based constraints here. All you need is to constrain the width and the height together, maintaining a given aspect ratio (in that case 4/3).

We can debate whether it's a good thing views know this information or whether should their parents set this kind of constraints. I usually prefer parents to set constraints, but if your view doesn't make any sense without this constraint or all these views need this constraint, you can safely let these views manage their own width/height ratio.

Finally, intrinsicContentSize tells Auto Layout the size of the view when the view is alone:

Returns the natural size for the receiving view, considering only properties of the view itself.

I don't know what your view represents, but as the documentation says, you can return CGSizeMake(UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric):

If a custom view has no intrinsic size for a given dimension, it can return UIViewNoIntrinsicMetric for that dimension.

0
votes

If you have the situation where you want to calculate the value of a constraint during runtime, the best option is to CTRL drag the constraint into the .h file of your controller and create an IBOutlet for it. This allows you to change the value of a constraint in code. You can even animate the change to a constraint value.

Then in your code at setup time or when an action occurs which might change the value you want (like loading a new image for example) you calculate the value you want for the constraint and set its value in the code. Usually you do this using:

self.myConstraintIBOutlet.constant = <insert your new value>;

You may then need to mark the affected view as needing layout:

// Mark whole view as needing layout. You could do this in a subview if
// only a small area is affected
[self.view setNeedsLayout];
[self.view layoutIfNeeded];

If you want a smooth transition, you can put layoutIfNeeded inside an animation block causing it to animate the change of constraint:

[self.view setNeedsLayout];
[UIView animateWithDuration:.25 animations:^{
            [self.view layoutIfNeeded];
        } completion:^(BOOL finished) {
            // Completion code
        }];