--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/MGSplitViewController.m Thu Feb 09 18:59:46 2012 +0100
@@ -0,0 +1,1133 @@
+//
+// MGSplitViewController.m
+// MGSplitView
+//
+// Created by Matt Gemmell on 26/07/2010.
+// Copyright 2010 Instinctive Code.
+//
+
+#import "MGSplitViewController.h"
+#import "MGSplitDividerView.h"
+#import "MGSplitCornersView.h"
+
+#define MG_DEFAULT_SPLIT_POSITION 320.0 // default width of master view in UISplitViewController.
+#define MG_DEFAULT_SPLIT_WIDTH 1.0 // default width of split-gutter in UISplitViewController.
+#define MG_DEFAULT_CORNER_RADIUS 5.0 // default corner-radius of overlapping split-inner corners on the master and detail views.
+#define MG_DEFAULT_CORNER_COLOR [UIColor blackColor] // default color of intruding inner corners (and divider background).
+
+#define MG_PANESPLITTER_CORNER_RADIUS 0.0 // corner-radius of split-inner corners for MGSplitViewDividerStylePaneSplitter style.
+#define MG_PANESPLITTER_SPLIT_WIDTH 25.0 // width of split-gutter for MGSplitViewDividerStylePaneSplitter style.
+
+#define MG_MIN_VIEW_WIDTH 200.0 // minimum width a view is allowed to become as a result of changing the splitPosition.
+
+#define MG_ANIMATION_CHANGE_SPLIT_ORIENTATION @"ChangeSplitOrientation" // Animation ID for internal use.
+#define MG_ANIMATION_CHANGE_SUBVIEWS_ORDER @"ChangeSubviewsOrder" // Animation ID for internal use.
+
+
+@interface MGSplitViewController (MGPrivateMethods)
+
+- (void)setup;
+- (CGSize)splitViewSizeForOrientation:(UIInterfaceOrientation)theOrientation;
+- (void)layoutSubviews;
+- (void)layoutSubviewsWithAnimation:(BOOL)animate;
+- (void)layoutSubviewsForInterfaceOrientation:(UIInterfaceOrientation)theOrientation withAnimation:(BOOL)animate;
+- (BOOL)shouldShowMasterForInterfaceOrientation:(UIInterfaceOrientation)theOrientation;
+- (BOOL)shouldShowMaster;
+- (NSString *)nameOfInterfaceOrientation:(UIInterfaceOrientation)theOrientation;
+- (void)reconfigureForMasterInPopover:(BOOL)inPopover;
+
+@end
+
+
+@implementation MGSplitViewController
+
+
+#pragma mark -
+#pragma mark Orientation helpers
+
+
+- (NSString *)nameOfInterfaceOrientation:(UIInterfaceOrientation)theOrientation
+{
+ NSString *orientationName = nil;
+ switch (theOrientation) {
+ case UIInterfaceOrientationPortrait:
+ orientationName = @"Portrait"; // Home button at bottom
+ break;
+ case UIInterfaceOrientationPortraitUpsideDown:
+ orientationName = @"Portrait (Upside Down)"; // Home button at top
+ break;
+ case UIInterfaceOrientationLandscapeLeft:
+ orientationName = @"Landscape (Left)"; // Home button on left
+ break;
+ case UIInterfaceOrientationLandscapeRight:
+ orientationName = @"Landscape (Right)"; // Home button on right
+ break;
+ default:
+ break;
+ }
+
+ return orientationName;
+}
+
+
+- (BOOL)isLandscape
+{
+ return UIInterfaceOrientationIsLandscape(self.interfaceOrientation);
+}
+
+
+- (BOOL)shouldShowMasterForInterfaceOrientation:(UIInterfaceOrientation)theOrientation
+{
+ // Returns YES if master view should be shown directly embedded in the splitview, instead of hidden in a popover.
+ return ((UIInterfaceOrientationIsLandscape(theOrientation)) ? _showsMasterInLandscape : _showsMasterInPortrait);
+}
+
+
+- (BOOL)shouldShowMaster
+{
+ return [self shouldShowMasterForInterfaceOrientation:self.interfaceOrientation];
+}
+
+
+- (BOOL)isShowingMaster
+{
+ return [self shouldShowMaster] && self.masterViewController && self.masterViewController.view && ([self.masterViewController.view superview] == self.view);
+}
+
+
+#pragma mark -
+#pragma mark Setup and Teardown
+
+
+- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
+{
+ if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) {
+ [self setup];
+ }
+
+ return self;
+}
+
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ if ((self = [super initWithCoder:aDecoder])) {
+ [self setup];
+ }
+
+ return self;
+}
+
+
+- (void)setup
+{
+ // Configure default behaviour.
+ _viewControllers = [[NSMutableArray alloc] initWithObjects:[NSNull null], [NSNull null], nil];
+ _splitWidth = MG_DEFAULT_SPLIT_WIDTH;
+ _showsMasterInPortrait = NO;
+ _showsMasterInLandscape = YES;
+ _reconfigurePopup = NO;
+ _vertical = YES;
+ _masterBeforeDetail = YES;
+ _splitPosition = MG_DEFAULT_SPLIT_POSITION;
+ CGRect divRect = self.view.bounds;
+ if ([self isVertical]) {
+ divRect.origin.y = _splitPosition;
+ divRect.size.height = _splitWidth;
+ } else {
+ divRect.origin.x = _splitPosition;
+ divRect.size.width = _splitWidth;
+ }
+ _dividerView = [[MGSplitDividerView alloc] initWithFrame:divRect];
+ _dividerView.splitViewController = self;
+ _dividerView.backgroundColor = MG_DEFAULT_CORNER_COLOR;
+ _dividerStyle = MGSplitViewDividerStyleThin;
+}
+
+
+- (void)dealloc
+{
+ _delegate = nil;
+ [self.view.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
+ [_viewControllers release];
+ [_barButtonItem release];
+ [_hiddenPopoverController release];
+ [_dividerView release];
+ [_cornerViews release];
+
+ [super dealloc];
+}
+
+
+#pragma mark -
+#pragma mark View management
+
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ return YES;
+}
+
+
+- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
+{
+ [self.masterViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ [self.detailViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
+}
+
+
+- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
+{
+ [self.masterViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+ [self.detailViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+}
+
+
+- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
+ duration:(NSTimeInterval)duration
+{
+ [self.masterViewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ [self.detailViewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+
+ // Hide popover.
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ // Re-tile views.
+ _reconfigurePopup = YES;
+ [self layoutSubviewsForInterfaceOrientation:toInterfaceOrientation withAnimation:YES];
+}
+
+
+- (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
+{
+ [self.masterViewController willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+ [self.detailViewController willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
+}
+
+
+- (void)didAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
+{
+ [self.masterViewController didAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation];
+ [self.detailViewController didAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation];
+}
+
+
+- (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration
+{
+ [self.masterViewController willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
+ [self.detailViewController willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration];
+}
+
+
+- (CGSize)splitViewSizeForOrientation:(UIInterfaceOrientation)theOrientation
+{
+ UIScreen *screen = [UIScreen mainScreen];
+ CGRect fullScreenRect = screen.bounds; // always implicitly in Portrait orientation.
+ CGRect appFrame = screen.applicationFrame;
+
+ // Find status bar height by checking which dimension of the applicationFrame is narrower than screen bounds.
+ // Little bit ugly looking, but it'll still work even if they change the status bar height in future.
+ float statusBarHeight = MAX((fullScreenRect.size.width - appFrame.size.width), (fullScreenRect.size.height - appFrame.size.height));
+
+ // Initially assume portrait orientation.
+ float width = fullScreenRect.size.width;
+ float height = fullScreenRect.size.height;
+
+ // Correct for orientation.
+ if (UIInterfaceOrientationIsLandscape(theOrientation)) {
+ width = height;
+ height = fullScreenRect.size.width;
+ }
+
+ // Account for status bar, which always subtracts from the height (since it's always at the top of the screen).
+ height -= statusBarHeight;
+
+ return CGSizeMake(width, height);
+}
+
+
+- (void)layoutSubviewsForInterfaceOrientation:(UIInterfaceOrientation)theOrientation withAnimation:(BOOL)animate
+{
+ if (_reconfigurePopup) {
+ [self reconfigureForMasterInPopover:![self shouldShowMasterForInterfaceOrientation:theOrientation]];
+ }
+
+ // Layout the master, detail and divider views appropriately, adding/removing subviews as needed.
+ // First obtain relevant geometry.
+ CGSize fullSize = [self splitViewSizeForOrientation:theOrientation];
+ float width = fullSize.width;
+ float height = fullSize.height;
+
+ if (NO) { // Just for debugging.
+ NSLog(@"Target orientation is %@, dimensions will be %.0f x %.0f",
+ [self nameOfInterfaceOrientation:theOrientation], width, height);
+ }
+
+ // Layout the master, divider and detail views.
+ CGRect newFrame = CGRectMake(0, 0, width, height);
+ UIViewController *controller;
+ UIView *theView;
+ BOOL shouldShowMaster = [self shouldShowMasterForInterfaceOrientation:theOrientation];
+ BOOL masterFirst = [self isMasterBeforeDetail];
+ if ([self isVertical]) {
+ // Master on left, detail on right (or vice versa).
+ CGRect masterRect, dividerRect, detailRect;
+ if (masterFirst) {
+ if (!shouldShowMaster) {
+ // Move off-screen.
+ newFrame.origin.x -= (_splitPosition + _splitWidth);
+ }
+
+ newFrame.size.width = _splitPosition;
+ masterRect = newFrame;
+
+ newFrame.origin.x += newFrame.size.width;
+ newFrame.size.width = _splitWidth;
+ dividerRect = newFrame;
+
+ newFrame.origin.x += newFrame.size.width;
+ newFrame.size.width = width - newFrame.origin.x;
+ detailRect = newFrame;
+
+ } else {
+ if (!shouldShowMaster) {
+ // Move off-screen.
+ newFrame.size.width += (_splitPosition + _splitWidth);
+ }
+
+ newFrame.size.width -= (_splitPosition + _splitWidth);
+ detailRect = newFrame;
+
+ newFrame.origin.x += newFrame.size.width;
+ newFrame.size.width = _splitWidth;
+ dividerRect = newFrame;
+
+ newFrame.origin.x += newFrame.size.width;
+ newFrame.size.width = _splitPosition;
+ masterRect = newFrame;
+ }
+
+ // Position master.
+ controller = self.masterViewController;
+ if (controller && [controller isKindOfClass:[UIViewController class]]) {
+ theView = controller.view;
+ if (theView) {
+ theView.frame = masterRect;
+ if (!theView.superview) {
+ [controller viewWillAppear:NO];
+ [self.view addSubview:theView];
+ [controller viewDidAppear:NO];
+ }
+ }
+ }
+
+ // Position divider.
+ theView = _dividerView;
+ theView.frame = dividerRect;
+ if (!theView.superview) {
+ [self.view addSubview:theView];
+ }
+
+ // Position detail.
+ controller = self.detailViewController;
+ if (controller && [controller isKindOfClass:[UIViewController class]]) {
+ theView = controller.view;
+ if (theView) {
+ theView.frame = detailRect;
+ if (!theView.superview) {
+ [self.view insertSubview:theView aboveSubview:self.masterViewController.view];
+ } else {
+ [self.view bringSubviewToFront:theView];
+ }
+ }
+ }
+
+ } else {
+ // Master above, detail below (or vice versa).
+ CGRect masterRect, dividerRect, detailRect;
+ if (masterFirst) {
+ if (!shouldShowMaster) {
+ // Move off-screen.
+ newFrame.origin.y -= (_splitPosition + _splitWidth);
+ }
+
+ newFrame.size.height = _splitPosition;
+ masterRect = newFrame;
+
+ newFrame.origin.y += newFrame.size.height;
+ newFrame.size.height = _splitWidth;
+ dividerRect = newFrame;
+
+ newFrame.origin.y += newFrame.size.height;
+ newFrame.size.height = height - newFrame.origin.y;
+ detailRect = newFrame;
+
+ } else {
+ if (!shouldShowMaster) {
+ // Move off-screen.
+ newFrame.size.height += (_splitPosition + _splitWidth);
+ }
+
+ newFrame.size.height -= (_splitPosition + _splitWidth);
+ detailRect = newFrame;
+
+ newFrame.origin.y += newFrame.size.height;
+ newFrame.size.height = _splitWidth;
+ dividerRect = newFrame;
+
+ newFrame.origin.y += newFrame.size.height;
+ newFrame.size.height = _splitPosition;
+ masterRect = newFrame;
+ }
+
+ // Position master.
+ controller = self.masterViewController;
+ if (controller && [controller isKindOfClass:[UIViewController class]]) {
+ theView = controller.view;
+ if (theView) {
+ theView.frame = masterRect;
+ if (!theView.superview) {
+ [controller viewWillAppear:NO];
+ [self.view addSubview:theView];
+ [controller viewDidAppear:NO];
+ }
+ }
+ }
+
+ // Position divider.
+ theView = _dividerView;
+ theView.frame = dividerRect;
+ if (!theView.superview) {
+ [self.view addSubview:theView];
+ }
+
+ // Position detail.
+ controller = self.detailViewController;
+ if (controller && [controller isKindOfClass:[UIViewController class]]) {
+ theView = controller.view;
+ if (theView) {
+ theView.frame = detailRect;
+ if (!theView.superview) {
+ [self.view insertSubview:theView aboveSubview:self.masterViewController.view];
+ } else {
+ [self.view bringSubviewToFront:theView];
+ }
+ }
+ }
+ }
+
+ // Create corner views if necessary.
+ MGSplitCornersView *leadingCorners; // top/left of screen in vertical/horizontal split.
+ MGSplitCornersView *trailingCorners; // bottom/right of screen in vertical/horizontal split.
+ if (!_cornerViews) {
+ CGRect cornerRect = CGRectMake(0, 0, 10, 10); // arbitrary, will be resized below.
+ leadingCorners = [[MGSplitCornersView alloc] initWithFrame:cornerRect];
+ leadingCorners.splitViewController = self;
+ leadingCorners.cornerBackgroundColor = MG_DEFAULT_CORNER_COLOR;
+ leadingCorners.cornerRadius = MG_DEFAULT_CORNER_RADIUS;
+ trailingCorners = [[MGSplitCornersView alloc] initWithFrame:cornerRect];
+ trailingCorners.splitViewController = self;
+ trailingCorners.cornerBackgroundColor = MG_DEFAULT_CORNER_COLOR;
+ trailingCorners.cornerRadius = MG_DEFAULT_CORNER_RADIUS;
+ _cornerViews = [[NSArray alloc] initWithObjects:leadingCorners, trailingCorners, nil];
+ [leadingCorners release];
+ [trailingCorners release];
+
+ } else if ([_cornerViews count] == 2) {
+ leadingCorners = [_cornerViews objectAtIndex:0];
+ trailingCorners = [_cornerViews objectAtIndex:1];
+ }
+
+ // Configure and layout the corner-views.
+ leadingCorners.cornersPosition = (_vertical) ? MGCornersPositionLeadingVertical : MGCornersPositionLeadingHorizontal;
+ trailingCorners.cornersPosition = (_vertical) ? MGCornersPositionTrailingVertical : MGCornersPositionTrailingHorizontal;
+ leadingCorners.autoresizingMask = (_vertical) ? UIViewAutoresizingFlexibleBottomMargin : UIViewAutoresizingFlexibleRightMargin;
+ trailingCorners.autoresizingMask = (_vertical) ? UIViewAutoresizingFlexibleTopMargin : UIViewAutoresizingFlexibleLeftMargin;
+
+ float x, y, cornersWidth, cornersHeight;
+ CGRect leadingRect, trailingRect;
+ float radius = leadingCorners.cornerRadius;
+ if (_vertical) { // left/right split
+ cornersWidth = (radius * 2.0) + _splitWidth;
+ cornersHeight = radius;
+ x = ((shouldShowMaster) ? ((masterFirst) ? _splitPosition : width - (_splitPosition + _splitWidth)) : (0 - _splitWidth)) - radius;
+ y = 0;
+ leadingRect = CGRectMake(x, y, cornersWidth, cornersHeight); // top corners
+ trailingRect = CGRectMake(x, (height - cornersHeight), cornersWidth, cornersHeight); // bottom corners
+
+ } else { // top/bottom split
+ x = 0;
+ y = ((shouldShowMaster) ? ((masterFirst) ? _splitPosition : height - (_splitPosition + _splitWidth)) : (0 - _splitWidth)) - radius;
+ cornersWidth = radius;
+ cornersHeight = (radius * 2.0) + _splitWidth;
+ leadingRect = CGRectMake(x, y, cornersWidth, cornersHeight); // left corners
+ trailingRect = CGRectMake((width - cornersWidth), y, cornersWidth, cornersHeight); // right corners
+ }
+
+ leadingCorners.frame = leadingRect;
+ trailingCorners.frame = trailingRect;
+
+ // Ensure corners are visible and frontmost.
+ if (!leadingCorners.superview) {
+ [self.view insertSubview:leadingCorners aboveSubview:self.detailViewController.view];
+ [self.view insertSubview:trailingCorners aboveSubview:self.detailViewController.view];
+ } else {
+ [self.view bringSubviewToFront:leadingCorners];
+ [self.view bringSubviewToFront:trailingCorners];
+ }
+}
+
+
+- (void)layoutSubviewsWithAnimation:(BOOL)animate
+{
+ [self layoutSubviewsForInterfaceOrientation:self.interfaceOrientation withAnimation:animate];
+}
+
+
+- (void)layoutSubviews
+{
+ [self layoutSubviewsForInterfaceOrientation:self.interfaceOrientation withAnimation:YES];
+}
+
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ [super viewWillAppear:animated];
+
+ if ([self isShowingMaster]) {
+ [self.masterViewController viewWillAppear:animated];
+ }
+ [self.detailViewController viewWillAppear:animated];
+
+ _reconfigurePopup = YES;
+ [self layoutSubviews];
+}
+
+
+- (void)viewDidAppear:(BOOL)animated
+{
+ [super viewDidAppear:animated];
+
+ if ([self isShowingMaster]) {
+ [self.masterViewController viewDidAppear:animated];
+ }
+ [self.detailViewController viewDidAppear:animated];
+}
+
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+ [super viewWillDisappear:animated];
+
+ if ([self isShowingMaster]) {
+ [self.masterViewController viewWillDisappear:animated];
+ }
+ [self.detailViewController viewWillDisappear:animated];
+}
+
+
+- (void)viewDidDisappear:(BOOL)animated
+{
+ [super viewDidDisappear:animated];
+
+ if ([self isShowingMaster]) {
+ [self.masterViewController viewDidDisappear:animated];
+ }
+ [self.detailViewController viewDidDisappear:animated];
+}
+
+
+#pragma mark -
+#pragma mark Popover handling
+
+
+- (void)reconfigureForMasterInPopover:(BOOL)inPopover
+{
+ _reconfigurePopup = NO;
+
+ if ((inPopover && _hiddenPopoverController) || (!inPopover && !_hiddenPopoverController) || !self.masterViewController) {
+ // Nothing to do.
+ return;
+ }
+
+ if (inPopover && !_hiddenPopoverController && !_barButtonItem) {
+ // Create and configure popover for our masterViewController.
+ [_hiddenPopoverController release];
+ _hiddenPopoverController = nil;
+ [self.masterViewController viewWillDisappear:NO];
+ _hiddenPopoverController = [[UIPopoverController alloc] initWithContentViewController:self.masterViewController];
+ [self.masterViewController viewDidDisappear:NO];
+
+ // Create and configure _barButtonItem.
+ _barButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Master", nil)
+ style:UIBarButtonItemStyleBordered
+ target:self
+ action:@selector(showMasterPopover:)];
+
+ // Inform delegate of this state of affairs.
+ if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willHideViewController:withBarButtonItem:forPopoverController:)]) {
+ [(NSObject <MGSplitViewControllerDelegate> *)_delegate splitViewController:self
+ willHideViewController:self.masterViewController
+ withBarButtonItem:_barButtonItem
+ forPopoverController:_hiddenPopoverController];
+ }
+
+ } else if (!inPopover && _hiddenPopoverController && _barButtonItem) {
+ // I know this looks strange, but it fixes a bizarre issue with UIPopoverController leaving masterViewController's views in disarray.
+ [_hiddenPopoverController presentPopoverFromRect:CGRectZero inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO];
+
+ // Remove master from popover and destroy popover, if it exists.
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ [_hiddenPopoverController release];
+ _hiddenPopoverController = nil;
+
+ // Inform delegate that the _barButtonItem will become invalid.
+ if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willShowViewController:invalidatingBarButtonItem:)]) {
+ [(NSObject <MGSplitViewControllerDelegate> *)_delegate splitViewController:self
+ willShowViewController:self.masterViewController
+ invalidatingBarButtonItem:_barButtonItem];
+ }
+
+ // Destroy _barButtonItem.
+ [_barButtonItem release];
+ _barButtonItem = nil;
+
+ // Move master view.
+ UIView *masterView = self.masterViewController.view;
+ if (masterView && masterView.superview != self.view) {
+ [masterView removeFromSuperview];
+ }
+ }
+}
+
+
+- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController
+{
+ [self reconfigureForMasterInPopover:NO];
+}
+
+
+- (void)notePopoverDismissed
+{
+ [self popoverControllerDidDismissPopover:_hiddenPopoverController];
+}
+
+
+#pragma mark -
+#pragma mark Animations
+
+
+- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context
+{
+ if (([animationID isEqualToString:MG_ANIMATION_CHANGE_SPLIT_ORIENTATION] ||
+ [animationID isEqualToString:MG_ANIMATION_CHANGE_SUBVIEWS_ORDER])
+ && _cornerViews) {
+ for (UIView *corner in _cornerViews) {
+ corner.hidden = NO;
+ }
+ _dividerView.hidden = NO;
+ }
+}
+
+
+#pragma mark -
+#pragma mark IB Actions
+
+
+- (IBAction)toggleSplitOrientation:(id)sender
+{
+ BOOL showingMaster = [self isShowingMaster];
+ if (showingMaster) {
+ if (_cornerViews) {
+ for (UIView *corner in _cornerViews) {
+ corner.hidden = YES;
+ }
+ _dividerView.hidden = YES;
+ }
+ [UIView beginAnimations:MG_ANIMATION_CHANGE_SPLIT_ORIENTATION context:nil];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
+ }
+ self.vertical = (!self.vertical);
+ if (showingMaster) {
+ [UIView commitAnimations];
+ }
+}
+
+
+- (IBAction)toggleMasterBeforeDetail:(id)sender
+{
+ BOOL showingMaster = [self isShowingMaster];
+ if (showingMaster) {
+ if (_cornerViews) {
+ for (UIView *corner in _cornerViews) {
+ corner.hidden = YES;
+ }
+ _dividerView.hidden = YES;
+ }
+ [UIView beginAnimations:MG_ANIMATION_CHANGE_SUBVIEWS_ORDER context:nil];
+ [UIView setAnimationDelegate:self];
+ [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
+ }
+ self.masterBeforeDetail = (!self.masterBeforeDetail);
+ if (showingMaster) {
+ [UIView commitAnimations];
+ }
+}
+
+
+- (IBAction)toggleMasterView:(id)sender
+{
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ if (![self isShowingMaster]) {
+ // We're about to show the master view. Ensure it's in place off-screen to be animated in.
+ _reconfigurePopup = YES;
+ [self reconfigureForMasterInPopover:NO];
+ [self layoutSubviews];
+ }
+
+ // This action functions on the current primary orientation; it is independent of the other primary orientation.
+ [UIView beginAnimations:@"toggleMaster" context:nil];
+ if (self.isLandscape) {
+ self.showsMasterInLandscape = !_showsMasterInLandscape;
+ } else {
+ self.showsMasterInPortrait = !_showsMasterInPortrait;
+ }
+ [UIView commitAnimations];
+}
+
+
+- (IBAction)showMasterPopover:(id)sender
+{
+ if (_hiddenPopoverController && !(_hiddenPopoverController.popoverVisible)) {
+ // Inform delegate.
+ if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:popoverController:willPresentViewController:)]) {
+ [(NSObject <MGSplitViewControllerDelegate> *)_delegate splitViewController:self
+ popoverController:_hiddenPopoverController
+ willPresentViewController:self.masterViewController];
+ }
+
+ // Show popover.
+ [_hiddenPopoverController presentPopoverFromBarButtonItem:_barButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
+ }
+}
+
+
+#pragma mark -
+#pragma mark Accessors and properties
+
+
+- (id)delegate
+{
+ return _delegate;
+}
+
+
+- (void)setDelegate:(id <MGSplitViewControllerDelegate>)newDelegate
+{
+ if (newDelegate != _delegate &&
+ (!newDelegate || [(NSObject *)newDelegate conformsToProtocol:@protocol(MGSplitViewControllerDelegate)])) {
+ _delegate = newDelegate;
+ }
+}
+
+
+- (BOOL)showsMasterInPortrait
+{
+ return _showsMasterInPortrait;
+}
+
+
+- (void)setShowsMasterInPortrait:(BOOL)flag
+{
+ if (flag != _showsMasterInPortrait) {
+ _showsMasterInPortrait = flag;
+
+ if (![self isLandscape]) { // i.e. if this will cause a visual change.
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ // Rearrange views.
+ _reconfigurePopup = YES;
+ [self layoutSubviews];
+ }
+ }
+}
+
+
+- (BOOL)showsMasterInLandscape
+{
+ return _showsMasterInLandscape;
+}
+
+
+- (void)setShowsMasterInLandscape:(BOOL)flag
+{
+ if (flag != _showsMasterInLandscape) {
+ _showsMasterInLandscape = flag;
+
+ if ([self isLandscape]) { // i.e. if this will cause a visual change.
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ // Rearrange views.
+ _reconfigurePopup = YES;
+ [self layoutSubviews];
+ }
+ }
+}
+
+
+- (BOOL)isVertical
+{
+ return _vertical;
+}
+
+
+- (void)setVertical:(BOOL)flag
+{
+ if (flag != _vertical) {
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ _vertical = flag;
+
+ // Inform delegate.
+ if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willChangeSplitOrientationToVertical:)]) {
+ [_delegate splitViewController:self willChangeSplitOrientationToVertical:_vertical];
+ }
+
+ [self layoutSubviews];
+ }
+}
+
+
+- (BOOL)isMasterBeforeDetail
+{
+ return _masterBeforeDetail;
+}
+
+
+- (void)setMasterBeforeDetail:(BOOL)flag
+{
+ if (flag != _masterBeforeDetail) {
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ _masterBeforeDetail = flag;
+
+ if ([self isShowingMaster]) {
+ [self layoutSubviews];
+ }
+ }
+}
+
+
+- (float)splitPosition
+{
+ return _splitPosition;
+}
+
+
+- (void)setSplitPosition:(float)posn
+{
+ // Check to see if delegate wishes to constrain the position.
+ float newPosn = posn;
+ BOOL constrained = NO;
+ CGSize fullSize = [self splitViewSizeForOrientation:self.interfaceOrientation];
+ if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:constrainSplitPosition:splitViewSize:)]) {
+ newPosn = [_delegate splitViewController:self constrainSplitPosition:newPosn splitViewSize:fullSize];
+ constrained = YES; // implicitly trust delegate's response.
+
+ } else {
+ // Apply default constraints if delegate doesn't wish to participate.
+ float minPos = MG_MIN_VIEW_WIDTH;
+ float maxPos = ((_vertical) ? fullSize.width : fullSize.height) - (MG_MIN_VIEW_WIDTH + _splitWidth);
+ constrained = (newPosn != _splitPosition && newPosn >= minPos && newPosn <= maxPos);
+ }
+
+ if (constrained) {
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ _splitPosition = newPosn;
+
+ // Inform delegate.
+ if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willMoveSplitToPosition:)]) {
+ [_delegate splitViewController:self willMoveSplitToPosition:_splitPosition];
+ }
+
+ if ([self isShowingMaster]) {
+ [self layoutSubviews];
+ }
+ }
+}
+
+
+- (void)setSplitPosition:(float)posn animated:(BOOL)animate
+{
+ BOOL shouldAnimate = (animate && [self isShowingMaster]);
+ if (shouldAnimate) {
+ [UIView beginAnimations:@"SplitPosition" context:nil];
+ }
+ [self setSplitPosition:posn];
+ if (shouldAnimate) {
+ [UIView commitAnimations];
+ }
+}
+
+
+- (float)splitWidth
+{
+ return _splitWidth;
+}
+
+
+- (void)setSplitWidth:(float)width
+{
+ if (width != _splitWidth && width >= 0) {
+ _splitWidth = width;
+ if ([self isShowingMaster]) {
+ [self layoutSubviews];
+ }
+ }
+}
+
+
+- (NSArray *)viewControllers
+{
+ return [[_viewControllers copy] autorelease];
+}
+
+
+- (void)setViewControllers:(NSArray *)controllers
+{
+ if (controllers != _viewControllers) {
+ for (UIViewController *controller in _viewControllers) {
+ if ([controller isKindOfClass:[UIViewController class]]) {
+ [controller.view removeFromSuperview];
+ }
+ }
+ [_viewControllers release];
+ _viewControllers = [[NSMutableArray alloc] initWithCapacity:2];
+ if (controllers && [controllers count] >= 2) {
+ self.masterViewController = [controllers objectAtIndex:0];
+ self.detailViewController = [controllers objectAtIndex:1];
+ } else {
+ NSLog(@"Error: %@ requires 2 view-controllers. (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd));
+ }
+
+ [self layoutSubviews];
+ }
+}
+
+
+- (UIViewController *)masterViewController
+{
+ if (_viewControllers && [_viewControllers count] > 0) {
+ NSObject *controller = [_viewControllers objectAtIndex:0];
+ if ([controller isKindOfClass:[UIViewController class]]) {
+ return [[controller retain] autorelease];
+ }
+ }
+
+ return nil;
+}
+
+
+- (void)setMasterViewController:(UIViewController *)master
+{
+ if (!_viewControllers) {
+ _viewControllers = [[NSMutableArray alloc] initWithCapacity:2];
+ }
+
+ NSObject *newMaster = master;
+ if (!newMaster) {
+ newMaster = [NSNull null];
+ }
+
+ BOOL changed = YES;
+ if ([_viewControllers count] > 0) {
+ if ([_viewControllers objectAtIndex:0] == newMaster) {
+ changed = NO;
+ } else {
+ [_viewControllers replaceObjectAtIndex:0 withObject:newMaster];
+ }
+
+ } else {
+ [_viewControllers addObject:newMaster];
+ }
+
+ if (changed) {
+ [self layoutSubviews];
+ }
+}
+
+
+- (UIViewController *)detailViewController
+{
+ if (_viewControllers && [_viewControllers count] > 1) {
+ NSObject *controller = [_viewControllers objectAtIndex:1];
+ if ([controller isKindOfClass:[UIViewController class]]) {
+ return [[controller retain] autorelease];
+ }
+ }
+
+ return nil;
+}
+
+
+- (void)setDetailViewController:(UIViewController *)detail
+{
+ if (!_viewControllers) {
+ _viewControllers = [[NSMutableArray alloc] initWithCapacity:2];
+ [_viewControllers addObject:[NSNull null]];
+ }
+
+ BOOL changed = YES;
+ if ([_viewControllers count] > 1) {
+ if ([_viewControllers objectAtIndex:1] == detail) {
+ changed = NO;
+ } else {
+ [_viewControllers replaceObjectAtIndex:1 withObject:detail];
+ }
+
+ } else {
+ [_viewControllers addObject:detail];
+ }
+
+ if (changed) {
+ [self layoutSubviews];
+ }
+}
+
+
+- (MGSplitDividerView *)dividerView
+{
+ return [[_dividerView retain] autorelease];
+}
+
+
+- (void)setDividerView:(MGSplitDividerView *)divider
+{
+ if (divider != _dividerView) {
+ [_dividerView removeFromSuperview];
+ [_dividerView release];
+ _dividerView = [divider retain];
+ _dividerView.splitViewController = self;
+ _dividerView.backgroundColor = MG_DEFAULT_CORNER_COLOR;
+ if ([self isShowingMaster]) {
+ [self layoutSubviews];
+ }
+ }
+}
+
+
+- (BOOL)allowsDraggingDivider
+{
+ if (_dividerView) {
+ return _dividerView.allowsDragging;
+ }
+
+ return NO;
+}
+
+
+- (void)setAllowsDraggingDivider:(BOOL)flag
+{
+ if (self.allowsDraggingDivider != flag && _dividerView) {
+ _dividerView.allowsDragging = flag;
+ }
+}
+
+
+- (MGSplitViewDividerStyle)dividerStyle
+{
+ return _dividerStyle;
+}
+
+
+- (void)setDividerStyle:(MGSplitViewDividerStyle)newStyle
+{
+ if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) {
+ [_hiddenPopoverController dismissPopoverAnimated:NO];
+ }
+
+ // We don't check to see if newStyle equals _dividerStyle, because it's a meta-setting.
+ // Aspects could have been changed since it was set.
+ _dividerStyle = newStyle;
+
+ // Reconfigure general appearance and behaviour.
+ float cornerRadius;
+ if (_dividerStyle == MGSplitViewDividerStyleThin) {
+ cornerRadius = MG_DEFAULT_CORNER_RADIUS;
+ _splitWidth = MG_DEFAULT_SPLIT_WIDTH;
+ self.allowsDraggingDivider = NO;
+
+ } else if (_dividerStyle == MGSplitViewDividerStylePaneSplitter) {
+ cornerRadius = MG_PANESPLITTER_CORNER_RADIUS;
+ _splitWidth = MG_PANESPLITTER_SPLIT_WIDTH;
+ self.allowsDraggingDivider = YES;
+ }
+
+ // Update divider and corners.
+ [_dividerView setNeedsDisplay];
+ if (_cornerViews) {
+ for (MGSplitCornersView *corner in _cornerViews) {
+ corner.cornerRadius = cornerRadius;
+ }
+ }
+
+ // Layout all views.
+ [self layoutSubviews];
+}
+
+
+- (void)setDividerStyle:(MGSplitViewDividerStyle)newStyle animated:(BOOL)animate
+{
+ BOOL shouldAnimate = (animate && [self isShowingMaster]);
+ if (shouldAnimate) {
+ [UIView beginAnimations:@"DividerStyle" context:nil];
+ }
+ [self setDividerStyle:newStyle];
+ if (shouldAnimate) {
+ [UIView commitAnimations];
+ }
+}
+
+
+- (NSArray *)cornerViews
+{
+ if (_cornerViews) {
+ return [[_cornerViews retain] autorelease];
+ }
+
+ return nil;
+}
+
+
+@synthesize showsMasterInPortrait;
+@synthesize showsMasterInLandscape;
+@synthesize vertical;
+@synthesize delegate;
+@synthesize viewControllers;
+@synthesize masterViewController;
+@synthesize detailViewController;
+@synthesize dividerView;
+@synthesize splitPosition;
+@synthesize splitWidth;
+@synthesize allowsDraggingDivider;
+@synthesize dividerStyle;
+
+
+@end