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:
.secondarycolumns, typically the master and detail view controllers, are displayed for a
.regularhorizontal size class, such as an iPad.
.compactcolumn, which can be any sort of view controller you like, is displayed for a
.compacthorizontal 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
.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
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
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
.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.
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
.secondarycolumn is visible, occupying the entire interface.
.oneOverSecondary: There are two columns, either side by side (
beside) or with the
.secondarycolumn fullscreen and with the
oneas an overlay (
over). The name is coy about which column is the
onebecause this will be different depending on whether there are two columns or three columns!
If this is a double-column layout, the
If this is a triple-column layout, the
.twoDisplaceSecondary: Applicable only in a three-column layout.
twomeans both the
.supplementarycolumns appear; the difference is in how the
.secondarycolumn appears in relation to them:
beside: All three columns are side by side.
.supplementarycolumns constitute an overlay.
.secondarycolumn 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
hide(_:), which take a column as their parameter.
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:
displacedisplay mode is allowed.
displacedisplay mode is allowed.
overdisplay mode is allowed;
.twoBesideSecondaryis not allowed, but
.oneBesideSecondaryis allowed, as otherwise we’d never see the
.primarycolumn 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
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
The presence of the button that does the same thing.
Perhaps the easiest way to understand the implications of
presentsWithGestureis to think about what happens if you set this property to
false: the user then cannot hide the
.primarycolumn if it is showing in a
besidedisplay mode, and cannot summon the
.primarycolumn 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
true, then in a three-column layout, a button is present that allows the user to dismiss all columns except the
.secondarycolumn, or to summon the
.supplementarycolumn 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
.supplementaryview on the left and the
.secondaryview on the right; but the user cannot dismiss the
.supplementaryview entirely because there’s no button for that. (But the user can rotate the iPad to portrait mode, where the
.supplementaryview is an overlay, and now it is possible to dismiss the
Here are some further settings that let you customize the size and position of the pieces in the split view controller’s layout:
Which side the
.supplementarycolumn) appears on. Your choices are
.trailingsplit view controller is a rarity, but clearly, it isn’t illegal.
Sets the widths that the
.supplementarycolumns will have when showing.
width properties is a little tricky. Here are some tips:
To specify the default width, use
To let the
.supplementarycolumn fill the remainder of the screen, use
To learn the actual width being used, ask for the current
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.