25

While there are many questions and answers about building a storyboard layout that would work both on 4" and 3.5" screen sizes, I couldn't find a suitable solution for the following scenario.

I use autolayout with ios6 (and ios7), and I don't have to support landscpae or ipad. I have a UIView with several subviews, which have to look as the mockup below. It is easy to set up autolayout constraints in the storyboard to fit either of the screen sizes. My question is - how do I make autolayout choose the correct constraints depending on the screen size at runtime?

SCREEN MOCKUPS

Note, that I DONT want to use 2 different storyboards. Doing so across my whole application would be a lot of work, and I would have to hook up all the delegates, outlets and actions on each storyboard. A change in a screen would require me to do double the work.

I have tried 2 methods to make this work on one storyboard, but I'm not satisfied with either of them.

  • Double the constraints. The larger constraint (50) has a higher priority than the lower constraint (30). The problem with this approach is that on the 3.5" screen size, autolayout may pick a just few lower priority constraints - enough to satisfy the layout - but leave some high priority constraints.

double constraints

  • Subclass NSLayoutConstraint. In this method, all the constraints in the storyboard are set to be NSDualLayoutConstraint. In in initialization code of NSDualLayoutConstraint, the constant of the constraint is changed to the value of 3_5_constant in case the runtime screen size is 3.5". this method is more deterministic, but you can't see the 3.5" layout in the interface builder preview.

enter image description here


If only interface builder constraints had a secondary constant value that would be applied when the screen size is 3.5", it would solve my problem.. So I remain with my question - how can I properly use a single storyboard to layout its subviews correctly for 4" AND 3.5" screen sizes?

gardenofwine
  • 1,344
  • 2
  • 15
  • 24
  • I would advise you to do it programmatically in those cases! – Zalykr Oct 29 '13 at 12:30
  • @Heliem, doing it programmatically will require him to add conditions all over his ViewControllers to choose between 3.5'' and 4.0''. There's really no difference besides all the additional code. – ShayDavidson Oct 29 '13 at 12:41
  • @Indigon not exactly, I think he only has problems in those cases where he wants different constraints depending on screen size. If so, he could do only those programmatically – Zalykr Oct 29 '13 at 13:39
  • @Heliem, my second proposed solution consists of changing the constants programmatically; the difference is, that in my solution the values of the alternate values are set in interface builder as runtime attributes, and the 'switcharoo' code only exists in `NSLayoutConstraint` What you propose is much more code (IBOutlet for every constraint in the storyboard), and less visibility) – gardenofwine Oct 29 '13 at 14:19
  • Have you tried combining "less-or-equal" and "greater-or-equal" constraints? For example height of the big inner view "smaller or equal to 248" and "bigger or equal to 220". – patric.schenke Nov 19 '13 at 12:51
  • @patric.schenke I have tried that, but it gives even less consistent results than the 'double constraints' method. – gardenofwine Nov 19 '13 at 15:31
  • I can't believe how there's no proper solution in iOS for this yet. In Android (since it was released) for example you can provide different dimensions for arbitrary screen sizes in an xml file and they'll be picked automatically. Even CSS is better. iOS added size classes but they don't cover this. Your solution with the custom constraint class is good, at least better than having to set them programatically. You can improve it by using `@IBDesignable` and `@IBInspectable` to now have to fiddle with runtime attributes. – User May 01 '16 at 13:30
  • @gardenofwine and Ixx you can find the proper solution here: http://stackoverflow.com/a/37325714/2477632 – HamzaGhazouani May 19 '16 at 14:13

4 Answers4

6

If you want to use only one storyboard and you do not want to utilize auto layout, which would make your life a lot easier for what you've shown in your diagram, you will have to layout your views in code.

You will just need to detect if the user is running on a 4" device and layout your views accordingly.

#define IS_568_SCREEN (fabs((double)[[UIScreen mainScreen]bounds].size.height - (double)568) < DBL_EPSILON)

if (IS_568_SCREEN) {
    // Lots of code to layout for 4" devices
} else {
    // Lots of code to layout for 3.5" devices
}

However, if you were to use autolayout for this, you'd find it's going to save you a ton of time and code. Instead of having to manually layout every view in code using the solution I mentioned above, you'd simply need to update the y and height constraints depending on the device.

Considering this diagram showing what autolayout would handle for you and what you'd need to update manually, this should help paint a better picture of just how much you'll save with utilizing autolayout.

enter image description here

#define IS_568_SCREEN (fabs((double)[[UIScreen mainScreen]bounds].size.height - (double)568) < DBL_EPSILON)

if (IS_568_SCREEN) {
    self.layoutConstraintY.constant = 50.0f;
    self.layoutConstraintHeight.constant = 248.0f;
} else {
    self.layoutConstraintY.constant = 30.0f;
    self.layoutConstraintHeight.constant = 220.0f;
}
[self layoutIfNeeded];
Jeremy Fox
  • 2,668
  • 1
  • 25
  • 26
  • My question was indeed about autolayout, so I'm commenting about your second suggestion - which is to edit the constraints via code. That is a suggestion that I came up with in my question. See this comment I made to another responser: http://stackoverflow.com/questions/19658140/how-can-i-use-one-storyboard-for-4-and-3-5-iphone-screens-with-autolayout-ios/21285047?noredirect=1#comment29195363_19658140 – gardenofwine Jan 22 '14 at 15:32
  • 1
    Thanks for such a detailed answer – nicktones Mar 16 '14 at 22:31
  • The convoluted dance with `fabs()` and `DBL_EPSILON` is totally unnecessary. I've seen it copied around a bunch in the context of checking screen size, and it's based on a misunderstanding of the problems with floating point rounding. In this context, `==` is perfectly fine. (Also, you probably want to use an inequality, so that if Apple releases an even larger screen, it doesn't revert to the 3.5" layout.) – Paul Cantrell Aug 12 '14 at 19:24
2

This stackoverflow answer has solved the same problem for me.

Key is make an IBOutlet to the constraint (e.g height). Then do something like this

- (void)updateViewConstraints {
    [super updateViewConstraints];

    self.myConstraintFromIB.constant =
        [UIScreen mainScreen].bounds.size.height > 480.0f ? 200 : 100;
}
Community
  • 1
  • 1
carbonr
  • 6,049
  • 5
  • 46
  • 73
  • I had already suggested a better solution in my question (subclassing NSLayoutConstraint - NSDualLayoutConstraint) – gardenofwine Apr 29 '14 at 07:51
  • this is a bad answer, though its my own. Please understand your problem more and you will be able to solve it without doing a check like this. – carbonr Aug 12 '14 at 21:16
1

You will need to edit your constraints programatically. You may find this question helpful for that.

You could also have two separate Storyboard files, or two viewController scenes in one storyboard file.

Community
  • 1
  • 1
WolfLink
  • 3,308
  • 2
  • 26
  • 44
  • My second approach to the problem involves programmatically changing the constraints (by subclassing `NSLayoutconstraint`; And my question specifically asks for a solution with ONE storyboard. – gardenofwine Nov 23 '13 at 20:04
1

How about adding an extra view and including the square view inside it? The extra view should be transparent so no-one sees it. Set the constraints of the extra view to expand with the screen size and set the constraints of the square view to be centred in the extra box. Select a multiplier for the square view size relative to the extra box

Martin Lockett
  • 2,275
  • 27
  • 31