Force UIsplitviewcontroller to Always Show Master (Only) in Landscape (On iPhone 6 Plus)

Force UISplitViewController to always show master (only) in landscape (on iPhone 6 Plus)

You need to override the trait collection of your UISplitViewController to always have a compact size class. To do so you need to insert a container view controller as the parent of your UISplitViewController:

  1. Embed your UISplitViewController into a ContainerViewController
  2. Add the following code into your container view controller subclass to override the trait collection of your child view controller:

    class ContainerVC: UIViewController {
    override func viewDidLoad() {
    super.viewDidLoad()
    }

    override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
    performOverrideTraitCollection()
    }

    private func performOverrideTraitCollection() {
    for childVC in self.childViewControllers {
    setOverrideTraitCollection(UITraitCollection(horizontalSizeClass: .Compact), forChildViewController: childVC)
    }
    }}

Great explanation in Building Adaptive Apps with UIKit (WWDC 2014)

UISplitViewController always show master view in iPad portrait mode iOS 9

Subclass UISplitViewController

There is no need to specifically track orientation changes: Master and Detail will still be displayed in sequence on iPhone in portrait mode, and most iPhone in landscape mode.

preferredDisplayMode:.allVisible only affects the modes where both views can be simultaneously visible.

Swift

class SplitViewController: UISplitViewController {

override func viewDidLoad() {
super.viewDidLoad()

preferredDisplayMode = .allVisible
}
}

Obj-C

- (void)viewDidLoad {
[super viewDidLoad];
self.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;

}

iPad Portrait & iPhone 8 Plus Landscape

Sample Image

UISplitViewController in portrait on iPhone shows detail VC instead of master

Oh man, this was causing me a headache for a few days and could not figure out how to do this. The worst part was that creating a new Xcode iOS project with the master-detail template worked just fine. Fortunately, in the end, that little fact was how I found the solution.

There are some posts I've found that suggest that the solution is to implement the new primaryViewControllerForCollapsingSplitViewController: method on UISplitViewControllerDelegate. I tried that to no avail. What Apple does in the master-detail template that seems to work is implement the new (take a deep breath to say all of this one) splitViewController:collapseSecondaryViewController:ontoPrimaryViewController: delegate method (again on UISplitViewControllerDelegate). According to the docs, this method:

Asks the delegate to adjust the primary view controller and to incorporate the secondary view controller into the collapsed interface.

Make sure to read up on the discussion part of that method for more specific details.

The way that Apple handles this is:

- (BOOL)splitViewController:(UISplitViewController *)splitViewController
collapseSecondaryViewController:(UIViewController *)secondaryViewController
ontoPrimaryViewController:(UIViewController *)primaryViewController {

if ([secondaryViewController isKindOfClass:[UINavigationController class]]
&& [[(UINavigationController *)secondaryViewController topViewController] isKindOfClass:[DetailViewController class]]
&& ([(DetailViewController *)[(UINavigationController *)secondaryViewController topViewController] detailItem] == nil)) {

// Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded.
return YES;

} else {

return NO;

}
}

This implementation basically does the following:

  1. If secondaryViewController is what we're expecting (a UINavigationController), and it's showing what we're expecting (a DetailViewController -- your view controller), but has no model (detailItem), then "Return YES to indicate that we have handled the collapse by doing nothing; the secondary controller will be discarded."
  2. Otherwise, return "NO to let the split view controller try and incorporate the secondary view controller’s content into the collapsed interface"

The results are the following for the iPhone in portrait (either starting in portrait or rotating to portrait -- or more accurately compact size class):

  1. If your view is correct
    • and has a model, show the detail view controller
    • but has no model, show the master view controller
  2. If your view is not correct
    • show the master view controller

Clear as mud.

Disable Landscape for Split View Controller for iPhone in Universal App

I managed to figure it out by setting the supported app interface orientations at launch in the AppDelegate:

let deviceIdiom = UIScreen.mainScreen().traitCollection.userInterfaceIdiom

func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if deviceIdiom == .Pad {
return UIInterfaceOrientationMask.All
}
return UIInterfaceOrientationMask.Portrait
}

Open UISplitViewController to Master View rather than Detail

Swift

UISplitViewController display master view above detail in portrait orientation is not about showing the Master view, it is about presenting the Detail view in full width, underneath the Master view.

UISplitViewController in portrait on iPhone shows detail VC instead of master is about the principle of the collapse mechanism.

This present answer addresses:

  • Master → Detail (Compact width)
    • iPhone 4s, 5, 5s, SE, 6, 6s, 7 (any orientation)
    • iPod Touch
    • any iPhone Plus (portrait)
  • side-by-side (all other sizes)
    • iPad
    • any iPhone Plus (landscape)

You must set preferredDisplayMode. You would want is .primaryVisible if it existed! Using .allVisible, iOS picks Detail if only 1 view fits (Compact width); in that size, the code below will pick Master.

The trick is to change both the preferredDisplayMode to .allVisible and to return true in collapseSecondary:onto.

class PrimarySplitViewController: UISplitViewController,
UISplitViewControllerDelegate {

override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
self.preferredDisplayMode = .allVisible
}

func splitViewController(
_ splitViewController: UISplitViewController,
collapseSecondary secondaryViewController: UIViewController,
onto primaryViewController: UIViewController) -> Bool {
// Return true to prevent UIKit from applying its default behavior
return true
}
}

How can I implement the new iPad-like landscape mode on iPhone 6 plus

You might want to take a look at the "Building Adaptive Apps with UIKit" video on https://developer.apple.com/videos/wwdc/2014/

De basic idea is that the split view controller is used on iPhone and iPad. But when it detects that the device has a "Compact" horizontal size class it pushes the detailview on top of the master view, whereas with a "Regular" size class it shows the detailview next to the master view.

UISplitViewController in Universal storyboard doesn't show both on screen on iPhone 6 Plus

In order for apps to take full advantage of the iPhone 6 or 6+ screen size and for UISplitViewController to work as expected in landscape on the 6+, you must be sure to add the required iPhone 6 and 6+ launch images or setup a launch screen file.

Without those the app runs as if it is on an iPhone 5 or 5S (but scaled to fit the larger screen).



Related Topics



Leave a reply



Submit