NitWit Studios NitWit Studios - 4 months ago 5
iOS Question

iOS Autolayout Visual Format - How to set spacing to attempt one value but fall back to another for small screens?

I am creating a custom modal view that has a bunch of UILabels, UIImages, etc in it and will appear as a modal view. The view must use auto layout and must be done programmatically and not using storyboard/xibs (unfortunately).

The modal view grows with the content until it gets to 8 points from the edge (top, bottom, left, right) of the screen.

A lot of the margins between child views are 16 and 24 points. This is all great when on newer iPhones and in Portrait mode, and leaves plenty of space between the screen and the modal view.
But when rotated to landscape, the modal view hits the 8 pt limit from the top/bottom edges and a lot of the child views get shrunk completely.

The fix for this is changing all my "margins" between the child views to 8 points. This allows everything enough room to show.

My goal is to be able to dynamically adjust the margins so that they will "attempt" the higher value (i.e. 16, 24) but will "fall back" to a smaller value (i.e. 8) if the view is already filling the screen.

I know one method is to use multiple constraints, and just toggling them when the orientation is changed, but I don't want to go that route if possible, as it can be a pain to manage over time when changes are made.

I did find one tidbit a while back that said you could do something like this using priority values:

NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(24@200,8@900)-[titleLabel]-[detailsLabel]-(24@200,8@900)-[button(40)]-(24@200,8@900)-|", options: [.AlignAllCenterX], metrics: metrics, views: views))

However, this always falls back to the '8@900' value even when there is plenty of room for the view to grow in height.

I also tried this, which seems to work correctly for some of the child views, but not all of them:

NSLayoutConstraint.activateConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(>=8,<=24)-[titleLabel]-[detailsLabel]-(>=8,<=24)-[button(40)]-(>=8,<=24)-|", options: [.AlignAllCenterX], metrics: metrics, views: views))


It isn't falling back to 8, 8 is a higher priority constraint than 24 so it will do that if possible. In Auto Layout 1000 is considered required (i.e., if there are two mutually exclusive 1000 priority constraints, you'll see a bunch of errors in the log). Conversely, 0 is considered entirely optional.

I would consider solving your problem by making the smaller margin an inequality at required priority (1000) and the larger margin an equality at priority 999. That way it will attempt to use the 24 point value, but if it cannot do so while maintaining the other required constraints, it will make it as small as 8 points.

It's worth noting that both of those gaps will likely not be reduced at an equal rate. To make that happen you may have to do something tricky like using hidden views (hidden views still affect layout) that are pinned directly to the superview and the labels which have a required height of >=8, and also a height of 24@999. Then you can make those two hidden views also be constrained equal to one another.

NSLayoutConstraint.constraintsWithVisualFormat("V:|-(>=8,24@999)-[titleLabel]-[detailsLabel]-(>=8,24@999)-[button(40)]-(>=8, 24@999)-|", options: [.AlignAllCenterX], metrics: metrics, views: views)