The new UISplitViewController

uk In the spirit of the new iOS adaptive philosophy, the SDK itself is giving us a new (or an old modified) View Controller that is really adaptive: the UISplitViewController.

UISplitViewController is really a workhorse to be used in iOS 8” (from WWDC 2014 session “What’s New in Cocoa Touch”).

The big news about UISplitViewController (“Split”) in iOS8 is that it is not limited to the iPad anymore: we can now use it on the iPhone, too. We don’t have to think (and code) anymore about “which device we are on?”.

Collapsed

The first thing we note is a completely new (read-only) property that indicates the display mode of the split:

If that property is false the Split has the same appearance of the previous versions (< 8 on iPad) with two children view controllers (VC): the primary and the secondary. If the property is true (the split is collapsed) it has only a VC and the secondary does not exist anymore. This is the new state, when the Split behaves similarly to a single UINavigationController.

Note that if the Split is in the expanded state, it can possibly continue to show a single VC, but it is also capable of showing two. On the other hand it is not capable to show two VCs at the same moment when it is in the collapsed state.

How does the split decide to be collapsed or expanded?

The UISplitViewController decides to be expanded or collapsed based on its current trait collection (UITraitCollection)  (see our article about class sizes for details).

The rule is simple:

  • if the horizontal class size is compact the UISplitViewController is in collapsed mode
  • if the horizontal class size is regular the UISplitViewController is in expanded mode

For an app running on the iPhone thus the Split is collapsed in all orientations (but we will see shortly how to possibly modify this behavior).

showDetailViewController

In our project example (downloadable at the end of the article) we use the new API showDetailViewController to change the content of the detail child of the Split (we already spoke about showViewController and showDetailViewController in this article). Let us compare this with the code we used in the past.

Showing a new view controller in split detail (<= iOS7):

Showing a new view controller in split detail (>= iOS8):

The big difference here is that in the last case (iOS8) we instantiate a completely new detail VC instead of simply changing the photo of the current one (as in the first example). (Note that we instantiate from the storyboard but we could also use the init and configure the VC programmatically).

In theory we can use the first code also in iOS8:

but we should make assumption on the current configuration (“we are inside a split”), and the real code is more complicated because in the collapsed state a VC of class PhotoViewController could not exist.

Also Apple in his Sample Code (AdaptivePhotosAnAdaptiveApplication) instantiate the VC every time with code like this:

We think that the overload to reallocate every time a new VC can be  a price to pay in order to be really “adaptive”. Any comment and suggestion on this topic will be welcomed.

 Do you want a Split expanded on the iPhone too?

Now you can have it.

We said that the state of the split (collapsed or expanded) depends on the current trait collection of the UISplitViewController. What if we could make the split believe it is in horizontal regular class when it is really in a compact one?

A container view controller can override the trait collection of a child view controller using the new method:

Thus we implement a (very simple) container view controller that contains only the split  and override its trait collection.

This is the code of the container class, note that we override  the traits only when we are in landscape orientation (it’s okay to be collapsed in portrait):

Note that we set only the horizontal size class of the current trait (line 30 and line 9), so the other values (as the vertical dimension class) of the trait collection remain untouched.

Then we add this code to the app delegate:

A split collapsing

When the split moves from collapsed to expanding state, and viceversa, it calls our delegate object and we have the opportunity to modify this behaviour. (in our app we see this transition when we rotate the iPhone).

When the split collapses it gives us a chance to make something with the current secondary VC (that is going to be deleted). In this phase it calls the following delegate method:

If this method return false (or is not implemented at all) the split calls the method collapseSecondaryViewController:forSplitViewController of the primary VC so that it has a chance to do something. For example if the primary is a UINavigationController, it pushes the secondary on the stack as the top VC (other VCs do nothing in this case).

If we return true the split simply leaves the primary as the new single VC.

In the example app we return true from this method when no photo is selected in the primary VC and thus the secondary is empty, so it would not be useful to show it as the the new primary VC.

A split expanding

When a split expands it needs the new VC to put it in the new secondary section of the interface. In this phase it calls the delegate method:

If we return nil, the split calls the method separateSecondaryViewControllerForSplitViewController of the primary VC to obtain the new secondary. For example if the primary is a UINavigationController, it pops the last VC from its stack and returns it to the split.

If we return a new VC, this is used as the new secondary VC.

In the example app we use this method when no photo is selected and we want to show a special empty VC to remark this (it is a simple PhotoViewController with no photo inside).

Other interesting properties

There is a new property that we can use in an expanded Split to force the visibility of the two VCs:

With possible values:

And now it is also possible to modify the width of the primary/secondary sections using some new properties, such as:

by which we can set the proportion of the total width to assign to the primary controller.

The code

splitviewcontroller

 

EDIT 18 April 2016:

GitHub project now updated to Swift 2.2 (XCode 7.3)

Valerio Ferrucci

Valerio Ferrucci (valfer) develops software on Apple Macintosh since the 1990s until today for MacOS, OSX and, since some years, iOS. He is also a Web (PHP/MySQL/JS/CSS) and Android Developer.

More Posts - Website

Follow Me:
TwitterFacebookLinkedIn

  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  
  •  

4 thoughts on “The new UISplitViewController”

  1. Thank you very much! This was exactly the information I was digging for in Apple’s documentation for hours with no avail. Even the WWDC video of the session “Building Adaptive Apps with UIKit” stops on the topic right after mentioning a container VC in the trait hierarchy but without any details, and their accompanying sample code doesn’t contain a clue to the solution. I owe you a pint of beer or glass of wine (at least 😉

  2. Thank you so so much for the tip regarding UITraitCollection.

    if the horizontal class size is compact the UISplitViewController is in collapsed mode
    if the horizontal class size is regular the UISplitViewController is in expanded mode

    I had changed the traitcollection for iPhone 6+ and the code effectively relied on default values … end result was that the splitviewcontroller.collapsed value never changed after the first time as the splitViewController always thought the view was horizontal.

    So I added an else to my if. 🙂

    if (size.width > 414.0)
    _overrideTraitCollection = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassRegular];
    else
    _overrideTraitCollection = [UITraitCollection traitCollectionWithHorizontalSizeClass:UIUserInterfaceSizeClassCompact];

    Days and days and days of searching for this to even determine what the problem was… Thank you.

  3. Like the others said, your solution saved the day for me, and was the only one I found.

    I took the idea on trait collection and did the same by subclassing UISplitViewController. I also added a Q&A to stack overflow referencing this page along with the code I’m using:

    http://stackoverflow.com/q/33151380/1633251

    Thanks again!

  4. How can I implement a SplitViewController when it is not the initial ViewController?

    Let’s assume that we have this implementation.

    1. ViewController with a label
    2. When we click the labek on the initial ViewController we want to segue to SplitViewController.

Leave a Reply

Your email address will not be published. Required fields are marked *