When iOS 8 was released, UISplitViewController has a big upgrade – it is no longer restricted to only iPad. That means, you can use the same on iPhone, therefore building a truly universal app.
NSHipster has a good guide to UISplitViewController.
Here, I only want to point out the horrible bugs.
My Typical Setup

My storyboard consists of:
- a master/primary view in a
UINavigationViewController - a detail/secondary view in a
UINavigationViewController - both have a
UITextFieldpinned to the bottom
Not much code needed to demo the bugs. It’s all in the master ViewController as such:
class ViewController: UIViewController, UISplitViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
splitViewController?.delegate = self
// Display mode can be: AllVisible | PrimaryOverlay | etc
// Set to .PrimaryOverlay, and the bugs will come
self.splitViewController?.preferredDisplayMode = .PrimaryOverlay
}
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool {
// To show detail first for eg iPhone compact size
return false
}
}
I tested in simulator and real devices, running the latest iOS 9.1.
The BUGS
When preferredDisplayMode is set to PrimaryOverlay (or PrimaryHidden or Automatic), 2 bugs will be introduced:
- In iPad, swipe from left edge on detail will not show master!
- The controller view will auto adjust whenever keyboard is shown
In fact, only by setting to AllVisible then things work correctly.
Bug #1 - Not being able to swipe from left edge is irritating, but luckily you can still have displayModeButtonItem that can be added to your navigation bar.
Bug #2 - auto adjusting your view is much more irritating. It looks like a feature at first, because it will prevent keyboard from blocking your UITextField.
However, the behaviour is buggy in iPhone. By switching to landscape, then back to portrait (as shown in the video above), the view will not auto adjust now! Because of that, you can’t safely assume your view will be adjusted correctly. Neither can you add codes to prevent the keyboard from blocking other views.
What’s more, the animation is laggy..
Terribe. Terrible.
Bonus Bug
Actually, there could be a warning Unbalanced calls to begin/end appearance transitions if you simply set preferredDisplayMode.
You have to wrap it around a dispatch block..
// Why set the display mode in dispatch?
// It's a workaround: http://stackoverflow.com/a/28440974/242682
dispatch_async(dispatch_get_main_queue()) {
// Display mode can be: AllVisible | PrimaryOverlay | etc
// The bugs coming from the mode .PrimaryOverlay
self.splitViewController?.preferredDisplayMode = .PrimaryOverlay
}
It’s a warning, but show how buggy UISplitViewController is!
UPDATE
After conversing with an Apple engineer, here is what I knew.
preferredDisplayMode is merely your preferred mode. The actual mode depends on the device size class, as stated in doc.
Use this property to specify the display mode that you prefer to use. The split view controller makes every effort to adopt the interface you specify but may use a different type of interface if there is not enough space to support your preferred choice. If changing the value of this property leads to an actual change in the current display mode, the split view controller animates the resulting change.
Setting the value of this property to
UISplitViewControllerDisplayModeAutomaticcauses the split view controller to choose the most appropriate display mode for the currently available space. On iPad, this results in use of the modeUISplitViewControllerDisplayModePrimaryOverlayin portrait orientations andUISplitViewControllerDisplayModeAllVisiblein landscape orientations. The default value of this property isUISplitViewControllerDisplayModeAutomatic.
When you rotate to landscape (regular horizontally), the mode will change to AllVisible automatically. And even after you rotate to portrait, it will remain AllVisible!
Hence, if you want it to be always PrimaryOverlay, then you have to set preferredDisplayMode every time there is a rotation:
override func willAnimateRotationToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval) {
self.splitViewController?.preferredDisplayMode = .PrimaryOverlay
}
Okay, got that.
Now the last mystery is why would PrimaryOverlay cause the view to adjust when keyboard is shown?
This is because in PrimaryOverlay, the master view is presented within a popover, and popovers will automatically adjust in response to keyboard..