The iPad programming guide says that the splitView\'s left pane is fixed to 320 points. But 320 pixels for my master view controller is too much. I would like to reduce it a
// in UISplitViewController subclass
// let more space for detail in portrait mode
- (void)viewWillLayoutSubviews
{
CGFloat width;
if (UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication.statusBarOrientation)){
width = CGRectGetWidth(self.view.bounds) * 0.25f;
}
else {
width = CGRectGetWidth(self.view.bounds) * 0.33f;
}
width = (NSInteger)fminf(260, fmaxf(120, width));
self.minimumPrimaryColumnWidth = width;
self.maximumPrimaryColumnWidth = width;
[super viewWillLayoutSubviews];
}
In my case, I had to set both maximum and minimum to make this work
mySplitViewController.preferredDisplayMode = .allVisible;
mySplitViewController.maximumPrimaryColumnWidth = UIScreen.main.bounds.width/2;
mySplitViewController.minimumPrimaryColumnWidth = UIScreen.main.bounds.width/2;
None of the answers worked for me on iOS7, so I did some of my own research and created a working solution. This will involve subclassing UISplitViewController
for the full functionality.
I will present the answer as if we just created a new project for iPad with all device orientations and have set the custom UISplitViewController
as the main view controller.
Create your custom UISplitViewController
. In this example mine is called MySplitViewController
. All code will be based in MySplitViewController.m
.
We're going to need to access a method from the UISplitViewControllerDelegate
so add that and set the delegate. We'll also setup a delegate forwarder incase you need to call the delegate methods from another class.
@interface MySplitViewController () <UISplitViewControllerDelegate>
@property (nonatomic, weak) id<UISplitViewControllerDelegate> realDelegate;
@end
@implementation MySplitViewController
- (instancetype)init {
self = [super init];
if (self) {
self.delegate = self;
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
self.delegate = self;
}
return self;
}
- (void)setDelegate:(id<UISplitViewControllerDelegate>)delegate {
[super setDelegate:nil];
self.realDelegate = (delegate != self) ? delegate : nil;
[super setDelegate:delegate ? self : nil];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
id delegate = self.realDelegate;
return [super respondsToSelector:aSelector] || [delegate respondsToSelector:aSelector];
}
- (id)forwardingTargetForSelector:(SEL)aSelector {
id delegate = self.realDelegate;
return [delegate respondsToSelector:aSelector] ? delegate : [super forwardingTargetForSelector:aSelector];
}
Setup the master and detail view controllers.
- (void)viewDidLoad {
[super viewDidLoad];
UIViewController* masterViewController = [[UIViewController alloc] init];
masterViewController.view.backgroundColor = [UIColor yellowColor];
UIViewController* detailViewController = [[UIViewController alloc] init];
detailViewController.view.backgroundColor = [UIColor cyanColor];
self.viewControllers = @[masterViewController, detailViewController];
}
Lets add our desired width to a method for easy reference.
- (CGFloat)desiredWidth {
return 200.0f;
}
We'll manipulate the master view controller before presenting it.
- (void)splitViewController:(UISplitViewController *)svc popoverController:(UIPopoverController *)pc willPresentViewController:(UIViewController *)aViewController {
id realDelegate = self.realDelegate;
if ([realDelegate respondsToSelector:@selector(splitViewController:popoverController:willPresentViewController:)]) {
[realDelegate splitViewController:svc popoverController:pc willPresentViewController:aViewController];
}
CGRect rect = aViewController.view.frame;
rect.size.width = [self desiredWidth];
aViewController.view.frame = rect;
aViewController.view.superview.clipsToBounds = NO;
}
However, now we're left with a display like this.
So were going to override a private method. Yes a private method, it will still be acceptable in the App Store since its not an underscore private method.
- (CGFloat)leftColumnWidth {
return [self desiredWidth];
}
This deals with portrait mode. So a similar thing for -splitViewController:willShowViewController:invalidatingBarButtonItem:
and you should be set for landscape.
However none of this will be needed in iOS8. You'll be able to simply call a min and max width property!
If you subclass UISplitViewController, you can implement -viewDidLayoutSubviews
and adjust the width there. This is clean, no hacks or private APIs, and works even with rotation.
- (void)viewDidLayoutSubviews
{
const CGFloat kMasterViewWidth = 240.0;
UIViewController *masterViewController = [self.viewControllers objectAtIndex:0];
UIViewController *detailViewController = [self.viewControllers objectAtIndex:1];
if (detailViewController.view.frame.origin.x > 0.0) {
// Adjust the width of the master view
CGRect masterViewFrame = masterViewController.view.frame;
CGFloat deltaX = masterViewFrame.size.width - kMasterViewWidth;
masterViewFrame.size.width -= deltaX;
masterViewController.view.frame = masterViewFrame;
// Adjust the width of the detail view
CGRect detailViewFrame = detailViewController.view.frame;
detailViewFrame.origin.x -= deltaX;
detailViewFrame.size.width += deltaX;
detailViewController.view.frame = detailViewFrame;
[masterViewController.view setNeedsLayout];
[detailViewController.view setNeedsLayout];
}
}
In IOS 8.0 you can easily do this by doing the following:
1. In your MasterSplitViewController.h add
@property(nonatomic, assign) CGFloat maximumPrimaryColumnWidth NS_AVAILABLE_IOS(8_0);
2. In your MasterSplitViewController.m viewDidLoad method add
self.maximumPrimaryColumnWidth = 100;
self.splitViewController.maximumPrimaryColumnWidth = self.maximumPrimaryColumnWidth;
This is a really good, simple and easy feature of IOS 8.
Here is how I did this in iOS8 with Swift.
class MainSplitViewController: UISplitViewController {
override func viewDidLoad() {
self.preferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible
self.maximumPrimaryColumnWidth = 100 // specify your width here
}
}
If you need to change the width dynamically from within your master/detail view in the split view, then do something like this:
var splitViewController = self.splitViewController as MainSplitViewController
splitViewController.maximumPrimaryColumnWidth = 400