5

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?

Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
Marek R
  • 32,568
  • 6
  • 55
  • 140

3 Answers3

1

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.

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
0

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.

Community
  • 1
  • 1
Alessandro Vendruscolo
  • 14,493
  • 4
  • 32
  • 41
0

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
        }];
Abhishek
  • 6,912
  • 14
  • 59
  • 85
Rory McKinnel
  • 7,936
  • 2
  • 17
  • 28
  • this part is obvious for me. But where do I hook up this code? When `self.myConstraintIBOutlet.constant = self.someView.intrinsicContentSize.height / self.someView.intrinsicContentSize.width;` should be called. It should be just before all views layout is updated. – Marek R Apr 29 '15 at 10:24
  • If it only happens at first display time, `viewWillAppear` would be a starting point as the views have their sizes at this point. If you leave it later to `viewDidAppear` you would see the layout change which might look like a glitch. – Rory McKinnel Apr 29 '15 at 10:28
  • If you need it to happen after you change something like changing text, or a button label etc then do it after the change has affected the view you changed something in. This may require you to do the change, force layout on the view changed, then change your constraint now the updated width/height is known then layout again. – Rory McKinnel Apr 29 '15 at 10:33
  • For an aspect ratio constraint, the aspect ratio value is not in the `constant` of the constraint, it's in the `multiplier`. You can't change the `multiplier` of a constraint after it's been created. – Ken Thomases Apr 29 '15 at 15:57
  • @KenThomases Yes that is correct. Options are to replace the constraint with a new one each time or to set the height and width based on the calculated aspect ratio assuming one of the width/height is known. Would need to know the exact use case. For replacement, the IBOutlet can be used to identify the original and then use replacements from then on. That way storyboard will stay happy and not add any auto constraints. – Rory McKinnel Apr 29 '15 at 16:36