More About Split View Controllers in iOS 14

In an earlier article, I talked about how split view controllers (UISplitViewController) have been completely revamped in iOS 14. In particular, they “adapt” to the environment’s horizontal size class in a whole new way. A two-part split view has three “columns”, but they are not all displayed at the same time:

  • The .primary and .secondary columns, typically the master and detail view controllers, are displayed for a .regular horizontal size class, such as an iPad.

  • The .compact column, which can be any sort of view controller you like, is displayed for a .compact horizontal size class, such as an iPhone.

This architecture is nice and simple; the trick, as I explained in the earlier article, is what to do so as not to lose the user’s place within the app when the environment toggles between the .regular and .compact size class.

Now I want to summarize more of the general changes in how split view controllers work in iOS 14.

The Three-Column Trick

The most obvious change in the split view controller interface is that a split view controller can now have three columns simultaneously. This is totally new in iOS 14 and is evident the moment you start to use iOS 14 on an iPad. An excellent case in point is the Mail app.

In iOS 13, the Mail app is a split view where the master view controller is itself a navigation controller that actually navigates internally. Within this navigation controller, the root view controller lists mailboxes, and you tap one to see a list of messages in that mailbox — still in the master view controller. Then you can tap a message listing to read the actual message in the detail view controller.

But in iOS 14, the list of mailboxes and the list of messages in the selected mailbox are two different columns — the .primary column and the .supplementary column.


This sounds confusing, and I suppose in a way it is, but it will seem a lot more coherent when you know the following simple rule: in a three-column split view controller layout, the user might be able to see the .supplementary column without seeing the .primary column, but it is impossible to see the .primary column without also seeing the .supplementary column. So the .supplementary column, despite its name, is the important one; it’s the one that determines “where we are” in the detail view controller (the .secondary column).

Whether your split view controller will have two columns or three columns is something you must decide when you initialize the split view controller. The initializer is init(style:), and the style is either .doubleColumn or .tripleColumn. (A third option, .unspecified, actually means: “Revert to the split view controller architecture from iOS 13 and before.”) Trying to assign something to the split view controller’s .supplementary column in a .doubleColumn layout is illegal and will cause your app to crash.

Display Modes

In the above screenshot of the Mail app, look at how the three columns are laid out. There’s the .primary column at the left (mailboxes), followed by the .supplementary column to its right (mail messages). To the right of that, darkened and shoved partway off the screen, is the .secondary column displaying the content of the selected message.

That layout, and every possible layout, is a display mode. For example, the user who is looking at the Mail app in that screenshot can now slide the .supplementary column to the left, shoving the .primary column entirely off the screen. Now we have the familiar splitscreen layout with the list of messages on the left and the selected message on the right. That’s simply a different display mode.

You can manipulate the display mode in code. To learn the actual display mode being used, ask for the split view controller’s current displayMode. To alter the display mode, set the preferredDisplayMode property; use .automatic to allow the display mode to adopt its default value.

Possible values are:

  • .secondaryOnly: Just the .secondary column is visible, occupying the entire interface.

  • .oneBesideSecondary, .oneOverSecondary: There are two columns, either side by side (beside) or with the .secondary column fullscreen and with the one as an overlay (over). The name is coy about which column is the one because this will be different depending on whether there are two columns or three columns!

    • If this is a double-column layout, the one is the .primary column.

    • If this is a triple-column layout, the one is the .supplementary column.

  • .twoBesideSecondary, .twoOverSecondary, .twoDisplaceSecondary: Applicable only in a three-column layout. two means both the .primary and .supplementary columns appear; the difference is in how the .secondary column appears in relation to them:

  • beside: All three columns are side by side.

  • over: The .primary and .supplementary columns constitute an overlay.

  • displace: The .secondary column is beside the other two, but without being reduced in width — instead, it is darkened, as in an overlay mode, and pushed partway offscreen (as in the screenshot above).

Your preferred display mode is only a preference; the runtime remembers and applies it as appropriate in terms of the interface’s orientation and the user’s actions. If the goal is to change the display mode on just one occasion, you might be better off calling show(_:) or hide(_:), which take a column as their parameter.

Split Personality

My list of display modes didn’t quite tell the whole story, because it turns out that the split view controller’s response to your preferredDisplayMode setting is also mediated by its split behavior. This is a rule limiting what display modes are permitted.

To learn the actual split behavior in effect, ask for the split view controller’s current splitBehavior. To alter the split behavior, set the preferredSplitBehavior property; use .automatic to allow the split behavior to adopt its default value.

Possible values are:

  • tile: No over or displace display mode is allowed.
  • overlay: No beside or displace display mode is allowed.
  • displace: No over display mode is allowed; .twoBesideSecondary is not allowed, but .oneBesideSecondary is allowed, as otherwise we’d never see the .primary column in a double-column layout!

The point of the split behavior is that if you ask for a display mode that isn’t permitted by the split behavior, you’ll get a different display mode. For instance, if you ask for .oneBesideSecondary when the split behavior is .overlay, you’ll get .oneOverSecondary.

Buttons and Gestures

So far, I’ve been talking from the point of view of you, the programmer. Now I want to say something about the affordances that enable the user to alter the display mode. The user’s ability to switch between display modes using buttons and gestures is affected by two properties:

  • presentsWithGesture: The default is true. Despite the name, it actually affects two things:

    • The enablement of the swipe gesture that summons the .primary column.

    • The presence of the button that does the same thing.

    Perhaps the easiest way to understand the implications of presentsWithGesture is to think about what happens if you set this property to false: the user then cannot hide the .primary column if it is showing in a beside display mode, and cannot summon the .primary column at all if it is not showing; giving the user a way to do those things, if desired, would then be up to you.

  • showsSecondaryOnlyButton: The default is false. If true, then in a three-column layout, a button is present that allows the user to dismiss all columns except the .secondary column, or to summon the .supplementary column if it is not showing.

    A good place to see this feature honored in the breach is in the same Mail screenshot I already showed. From that situation, the user can tap on the right side of the screen to reduce things to a side by side layout with the .supplementary view on the left and the .secondary view on the right; but the user cannot dismiss the .supplementary view entirely because there’s no button for that. (But the user can rotate the iPad to portrait mode, where the .supplementary view is an overlay, and now it is possible to dismiss the .supplementary column.)

Other Tweaks

Here are some further settings that let you customize the size and position of the pieces in the split view controller’s layout:

  • primaryEdge:
    Which side the .primary column (and .supplementary column) appears on. Your choices are .leading and .trailing. A .trailing split view controller is a rarity, but clearly, it isn’t illegal.

  • preferredPrimaryColumnWidth, preferredPrimaryColumnWidthFraction
    minimumPrimaryColumnWidth, maximumPrimaryColumnWidth
    preferredSupplementaryColumnWidth, preferredSupplementaryColumnWidthFraction
    minimumSupplementaryColumnWidth, maximumSupplementaryColumnWidth:
    Sets the widths that the .primary and .supplementary columns will have when showing.

Using the width properties is a little tricky. Here are some tips:

  • To specify the default width, use UISplitViewController.automaticDimension.

  • To let the .supplementary column fill the remainder of the screen, use UISplitViewController.automaticSupplementaryFillDimension.

  • To learn the actual width being used, ask for the current primaryColumnWidth and supplementaryColumnWidth.

  • If you set both a fractional and an absolute width, the absolute width takes precedence.

  • And here’s the one that always mystifies me: You must set the preferred maximum width before any other width setting will take effect!

It’s All Good

If all of that seems confusing, the thing to keep in mind is that in general, the default settings are just great. The split view controller, right out of the box, has the “right” behaviors, the “right” gestures and buttons, the “right” display modes in all the various possible situations (different device types and orientations). You wouldn’t get into modifying those defaults unless you had a special purpose, and in that case, you’d explore things more deeply.

There’s a little more to know about split view controllers; in particular, don’t forget to investigate the delegate protocol, which gives you a lot of information about what the user is up to. But really, what I’ve said in this article and the previous one pretty much constitutes the whole of the story. In general, what I like about split view controllers in their new iOS 14 incarnation is that they are simple. That should make them more usable, from a programmer’s point of view, than ever before.

If you missed part 1, check it out here.

You Might Also Like…

Taking Control of Rotation Animations in iOS

We all love to have animation in our app’s interface. But sometimes writing even the simplest animation can be rather elusive. Rotation is a case in point. As you probably know, the way to rotate a view (or a layer) is to change its transform, applying a rotation transform to it. But certain rotations are …

Taking Control of Rotation Animations in iOS Read More »

    Sign Up

    Get more articles and exclusive content that takes your iOS skills to the next level.