Split view controllers do have references to their master and detail view controllers, via the viewControllers
property.
In iOS 7.x and below, the viewControllers
array should have exactly two view controller objects in it. The first object is the master view controller and the second object is the detail view controller.
In iOS 8.0 and above, the viewControllers
array has at least one view controller object in it – the master (or "primary") view controller. If a second view controller object is in the array, then it is the detail (or "secondary") view controller. When the split view controller is collapsed, only the master view controller is in this array, and when expanded it will contain both the master and the detail view controllers.
You can use the splitViewController
property of all view controllers to get your split view controller, and from there use the viewControllers
property to access either your master or detail view controllers, like so:
Swift:
let masterVC = self.splitViewController?.viewControllers.first
let detailVC = (self.splitViewController?.viewControllers.count > 1) ? self.splitViewController?.viewControllers[1] : nil
Objective-C:
UIViewController *masterVC = [self.splitViewController.viewControllers firstObject];
UIViewController *detailVC;
if (self.splitViewController.viewControllers.count > 1) {
detailVC = self.splitViewController.viewControllers[1];
}
The splitViewController
property works by walking up the view controller hierarchy and trying to find any split view controller that the calling view controller is in. If the view controller is not in a split view controller, then the property is nil
. It works the same as the navigationController
and tabBarController
view controller properties.
You can make the master and detail view controllers easier to access using an extension in Swift (or a category in Objective-C) on UISplitViewController
, like so (replacing all the xx_
's with your own prefix if you're using Objective-C):
Swift:
extension UISplitViewController {
var primaryViewController: UIViewController? {
return self.viewControllers.first
}
var secondaryViewController: UIViewController? {
return self.viewControllers.count > 1 ? self.viewControllers[1] : nil
}
}
Objective-C:
// UISplitViewController+ChildViewControllerAccess.h
@interface UISplitViewController (ChildViewControllerAccess)
@property (nonatomic, readonly) UIViewController *xx_primaryViewController;
@property (nonatomic, readonly) UIViewController *xx_secondaryViewController;
@end
// UISplitViewController+ChildViewControllerAccess.m
@implementation UISplitViewController (ChildViewControllerAccess)
- (UIViewController *)xx_primaryViewController
{
return self.viewControllers.firstObject;
}
- (UIViewController *)xx_secondaryViewController
{
return self.viewControllers.count > 1 ? self.viewControllers[1] : nil;
}
@end
You can then make use of these properties like so:
Swift:
func someFunctionInSomeViewControllerClass {
// Get the primary and secondary view controllers if
// this view controller is in a split view controller.
// These will be nil if this view controller is not a
// descendant of a split view controller.
var primaryVC = self.splitViewController?.primaryViewController
var secondaryVC = self.splitViewController?.secondaryViewController
// Do something with them
primaryVC?.title = "This is the primary VC"
secondaryVC?.title = "This is the secondary VC"
}
Objective-C:
#import "UISplitViewController+ChildViewControllerAccess.h"
[...]
- (void)someMethodInSomeViewControllerClass
{
// Get the primary and secondary view controllers if
// this view controller is in a split view controller.
// These will be nil if this view controller is not a
// descendant of a split view controller.
UIViewController *primaryVC = self.splitViewController.xx_primaryViewController;
UIViewController *secondaryVC = self.splitViewController.xx_secondaryViewController;
// Do something with them
primaryVC.title = @"This is the primary VC";
secondaryVC.title = @"This is the secondary VC";
}