an existing iOS 7+ app supports iPhone and iPad. Currently I am using two different Storyboards for iPhone and iPad layout which works fine. Now I am trying to convert this to a One-Storyboard-Layout using Adaptive Size Classes.
My problem is, that my iPhone and iPad layouts are quite different. They not only use different sizes and positions for their controls but also some totally different View Controllers.
Both Storyboards use a MMDrawerController
as initial VC. But from there on the control flow is different. While the iPad Storyboard uses a UISplitViewController
as base for all other ViewControllers the iPhone uses a UITabBarViewController
instead.
This is just one difference but there are others. How should these differences be handled in one Storyboard? To achive this, I would have to specific different segues depending on the current Size Class. But as far as I know this is not possible.
Some of the new iOS 8 adaptive Segues can behave differently depending on the Size Class but as far as I know it is not possible to specify different segues for different Size Classes. Additionally it is not possible to define different initial ViewControllers depending on the Size Class.
So the question is: Is it possible to specify two different layouts (including different Segues, ViewControllers, etc.) in one Storyboard?
The reason to switch to a single Storyboard is to support the new Split Screen and Slide Over features in iOS 9. Loading different Storyboards at startup depending on the the screen size / size class is easy. But switching to another Storyboard at runtime when the size class is changed dynamically is not possible. Is it?
On top of this, segue objects are used to prepare for the transition from one view controller to another. When a segue is triggered, before the visual transition occurs, the storyboard runtime invokes prepareForSegue:sender: method of the current view controller (in our example, it’s the RecipeBookViewController).
Size classes are traits assigned to user interface elements, like scenes or views. They provide a rough indication of the element’s size. Interface Builder lets you customize many of your layout’s features based on the current size class. The layout then automatically adapts as the size class changes.
When a segue is triggered, before the visual transition occurs, the storyboard runtime invokes prepareForSegue:sender: method of the current view controller (in our example, it’s the RecipeBookViewController). By implementing this method, we can pass any needed data to the view controller that is about to be displayed.
This is the second article of our Storyboards series. In the first tutorial, we introduced the Storyboards, which is a friendly feature in Xcode for designing user interface. If you’ve followed the tutorial from start to end, you should already build a simple recipe app with navigation interface.
I have just gone through similar pain, and found that the only real way to handle the larger differences is to create separate controllers and segues in storyboard as normal, but to execute them in code rather than relying on segue activation created in storyboard.
In my case I was using a side menu on both iPhone and iPad, but on iPad was using split view controllers for the main display versus UINavigationController on iPhone. An added complication is that on iOS8, UISplitViewController is supported on iPhone but not on iOS7 where it is treated as a UINavigationController.
As far as I know, you cannot use size class to automatically trigger a segue of the right type. However you can do that in code as long as you have a mechanism for making the choice. So you can still create segues for each size class, or better your mode of display for a given device type, and call the right one from code.
Your biggest issue will be the iOS9 split screen which seems to dynamically change the size class from iPad regularW/regularH to iPad compactW/regularH and back as you swipe. You will be ok for things like split views which will simply switch to look like a navigation controller stack. I can see no way though to switch to a tab bar say on the fly unless you pop back to the root, have the app delegate switch the root screen and navigate you back to the same place. Much will depend on exactly what you want to see happen in this case.
In the end my general rule of thumb in storyboard was:
0) In the App delegate, work out the type of device and set the root screen to be the correct storyboard controller entrypoint: e.g. split view on ipad, tab view on iPhone. It is handy to add some methods/properties to the app delegate for any controller to access to find out the current running mode. This will make life easier later when you need to decide which segue to fire.
1) Create individual controllers as required and always use Any/Any size class design view to create the bulk of the design. I started using different design views but found that got too tricky to manage. Especially as some controllers could appear in popovers where the size class on iPad (compact width) in not the same as when on the main screen (regular width).
2) Use size class adjustments for individual constraints, fonts etc as required.
3) For any view controller which may appear in a popover, precede it with a UINavigationController with a storyboard Id that can be used as the root of any popover.
4) For UISplitViewControllers create as normal using showDetail style segues.
5) For UIViewControllers which are used as detail views in split view controllers, but which may also need to be pushed on when used in a popover or on iPhone on iOS7, create push segues from their respective master detail controllers. You only need this if you every present content designed in a split view via a popover or when on iOS7 which has no split view on iPhone.
6) Create segues between your controllers, including iPad/iPhone specific segues. You can use storyboard activation if there is only one possible segue. Otherwise simply draw the segues between the controllers themselves.
7) For any controllers where buttons or cells trigger segues, you add target actions rather than segue triggers. In these actions you will call the appropriate segue manually.
8) In each view controller where a destination controller type is dependent on the type of display mode, write code which uses your app delegate run mode properties to make a decision on which segue to use and then call peformSegueWithIdentifier
with the identifier of the segue.
Non of this is particularly pretty but it seems a necessary evil, especially if you use split view controllers and popovers. The good thing is that you can at least see everything in one place.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With