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
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:
- If
secondaryViewController
is what we're expecting (aUINavigationController
), and it's showing what we're expecting (aDetailViewController
-- 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.
" - 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):
- If your view is correct
- and has a model, show the detail view controller
- but has no model, show the master view controller
- If your view is not correct
- show the master view controller
Clear as mud.
UISplitViewController in portrait on iPhone always show master and detail in iOS 8
UISplitViewController use show side-by-side only in horizontally regular environment (new TraitCollection size)
The split view controller determines the arrangement of its child view controllers based on the available space. In a horizontally regular environment, the split view controller presents its view controllers side-by-side whenever possible. In a horizontally compact environment, the split view controller acts more like a navigation controller, displaying the primary view controller initially and pushing or popping the secondary view controller as needed. You can also ask the split view controller to prefer a specific arrangement by assigning a value to the preferredDisplayMode property.
Solution.
You have to change TraitCollection of SplitViewController. How do this:
- Create a ViewController and add your SplitViewController as a child.
- Override traitCollection with size .Regular for your child (UISplitViewController)
ViewController Wrapper
class TraitOverrideViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureSplitVC()
}
private func configureSplitVC() {
let splitVC = self.childViewControllers[0] as UISplitViewController
setOverrideTraitCollection(UITraitCollection(horizontalSizeClass: .Regular), forChildViewController: splitVC)
}
}
In iOS 8 UISplitViewController uses Adaptive User Interfaces and TraitCollections to show its content.
It shows different style depending on the size of and type of the view. You can change by technic I explain above.
You can get more info in WWDC video about Building Adaptive Apps with UIKit
UISplitViewController display master view above detail in portrait orientation
Edit: It's not a duplicate. Answer discovered in the comments. The solution is to use preferredDisplayMode
on UISplitViewController and setting it to UISplitViewControllerDisplayModePrimaryOverlay
Left the original answer for context to the comments and posterity.
Original Answer
This is a duplicate of this: UISplitViewController in portrait on iPhone shows detail VC instead of master
For reference, the solution in that case was to have the view controller that implements UISplitViewControllerDelegate
use the following code:
- (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;
}
}
UISplitViewController: How force to show master popover in app launch? (portrait)
I struggled with this one for a while, and even now I'm not 100% happy with the solution, but it is the only thing I've been able to come up with, given the current constraints.
First, override the following delegate method:
- (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController
and use it to grab a reference to the bar button item, and store it in an iVar:
barButtonForMaster = barButtonItem;
Then, when you want to show the master view controller, make a call like this:
[barButtonForMaster.target performSelector: barButtonForMaster.action withObject: barButtonForMaster];
In case you want to perform this right at the start, then use some delay in order to prevent app crashing (thanks to the helpful comment):
[barButtonForMaster.target performSelector: barButtonForMaster.action withObject: barButtonForMaster afterDelay:1];
In that case you can perform the selector right in the split view delegate method.
UISplitViewController - set always visible master controller when
A UISplitViewController has a property called preferredDisplayMode
. You can set this to any one of these values:
- UISplitViewControllerDisplayModeAutomatic
- UISplitViewControllerDisplayModePrimaryHidden
- UISplitViewControllerDisplayModePrimaryOverlay
- UISplitViewControllerDisplayModeAllVisible
You are looking for UISplitViewControllerDisplayModeAllVisible
.
[self.splitViewController setPreferredDisplayMode:UISplitViewControllerDisplayModeAllVisible];
UISplitViewControllerDisplayModeAllVisible
The primary and secondary view controllers are displayed side-by-side onscreen.
Available in iOS 8.0 and later.
You can read more about the display modes here on Apple's documentation.
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:
- Embed your UISplitViewController into a ContainerViewController
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)
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
}
}
Related Topics
How to Implement a Thread Safe Hashtable (Phonebook) Data Structure in Swift
Swift 4.2+ Seeding a Random Number Generator
How to Get the Download Progress with the New Try Await Urlsession.Shared.Download(...)
Using Cocoa Nssavepanel in Sandbox Causes Assertion Failure
How to Subclass Custom Uiviewcontroller in Swift
Can't Able to Get Video Tracks from Avurlasset for Hls Videos(.M3U8 Format) for Avplayer
Accessing Multiple Audio Hardware Outputs/Channels Using Avfoundation and Swift
Swift 3D Touch iOS 10 Home Screen Quick Actions Share Item Missing
Bitwise Operations with Cgbitmapinfo and Cgimagealphainfo
Forcing Nspersistentcontainer Change Core Data
How to Share Both Image and Text Together in Swift
Collection View Cell Button Not Triggering Action
How to Create Type Erasing Weak References with Non-Optional Properties in Swift
iOS 10 App Crashes When Trying to Save Image to Photo Library
Confusion Regarding Overriding Class Properties in Swift