AlternateViews/ViewController.m
/* |
File: ViewController.m |
Abstract: A view controller with different views for portrait and landscape |
orientations. |
Version: 1.3 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2014 Apple Inc. All Rights Reserved. |
*/ |
#import "ViewController.h" |
#import "PortraitView.h" |
#import "LandscapeView.h" |
@interface ViewController () |
//! The view to use while in portrait orientation. This property is configured |
//! to be strong because the view it references may not always be in the view |
//! hierarchy. |
@property (nonatomic, strong) IBOutlet PortraitView *portraitView; |
//! The view to use while in landscape orientation. This property is configured |
//! to be strong because the view it references may not always be in the view |
//! hierarchy. |
@property (nonatomic, strong) IBOutlet LandscapeView *landscapeView; |
@end |
@implementation ViewController |
{ |
//! Holds a snapshot of the outgoing view used duration rotation, |
//! to smooth out the transition animation. |
__weak UIView *_rotationSnapshotView; |
} |
//| ---------------------------------------------------------------------------- |
// Our view only serves as a container for the orientation-specific views. |
// If you remove this method, subviews added to the view for this view |
// controller's scene in the storyboard will be visible in both orientations. |
// |
- (void)loadView |
{ |
self.view = [[UIView alloc] init]; |
} |
//| ---------------------------------------------------------------------------- |
- (void)viewWillAppear:(BOOL)animated |
{ |
[super viewWillAppear:animated]; |
// Display the correct view for the current interface orientation. |
// This is done here instead of in -viewDidLoad because the value of |
// interfaceOrientation is not final when -viewDidLoad called. |
{ |
UIView *viewForCurrentInterfaceOrientation = [self viewForInterfaceOrientation:self.interfaceOrientation]; |
if (viewForCurrentInterfaceOrientation.superview == nil) |
{ |
[self.portraitView removeFromSuperview]; |
[self.landscapeView removeFromSuperview]; |
[self.view addSubview:viewForCurrentInterfaceOrientation]; |
[self.view sendSubviewToBack:viewForCurrentInterfaceOrientation]; |
} |
} |
// If you've created separate UIView subclasses for your portrait and |
// landscape interfaces, then you should perform and orientation specific |
// setup in the -willMoveToSuperview: method of each respective subclass |
// instead of here. |
} |
//| ---------------------------------------------------------------------------- |
- (void)viewWillLayoutSubviews |
{ |
[super viewWillLayoutSubviews]; |
// Match the orientation-specific view's frame to our view's frame. |
// It's easier to update both here instead of checking which one is |
// actually in use at the current time. |
self.portraitView.frame = self.view.bounds; |
self.landscapeView.frame = self.view.bounds; |
// Forward the current values of self.topLayoutGuide and |
// self.bottomLayoutGuide to the orientation-specific views. |
// (See the comments in LayoutSupport.h for more information) |
// It's easier to update both here instead of checking which one is |
// actually in use at the current time. |
if ([self respondsToSelector:@selector(topLayoutGuide)]) |
{ |
self.portraitView.topLayoutGuideLength = self.topLayoutGuide.length; |
self.landscapeView.topLayoutGuideLength = self.topLayoutGuide.length; |
self.portraitView.bottomLayoutGuideLength = self.bottomLayoutGuide.length; |
self.landscapeView.bottomLayoutGuideLength = self.bottomLayoutGuide.length; |
} |
// iOS 6 does not have topLayoutGuide and bottomLayoutGuide. |
else |
{ |
self.portraitView.topLayoutGuideLength = 0; |
self.landscapeView.topLayoutGuideLength = 0; |
self.portraitView.bottomLayoutGuideLength = 0; |
self.landscapeView.bottomLayoutGuideLength = 0; |
} |
} |
#pragma mark - |
#pragma mark Rotation |
//| ---------------------------------------------------------------------------- |
// This method is called before |
// a) The system configures transaction to animate the rotation (changes |
// made here won't be animated) |
// b) The system resizes the window's root view for the new orientation. |
// |
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration |
{ |
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
UIView *viewForCurrentInterfaceOrientation = [self viewForInterfaceOrientation:self.interfaceOrientation]; |
UIView *viewForFinalInterfaceOrientation = [self viewForInterfaceOrientation:toInterfaceOrientation]; |
// Ignore rotating from Landscape-Left to Landscape-Right or vis-versa. |
if (viewForCurrentInterfaceOrientation != viewForFinalInterfaceOrientation) |
{ |
// Don't let an orientation-specific view ever be resized for the |
// orientation it does not support. Instead, a snapshot of the |
// outgoing view is swapped in before the rotation animation |
// begins. The snapshot will be stretched (or squished) to match our |
// view's changing frame . |
UIView *rotationSnapshotView; |
if ([viewForCurrentInterfaceOrientation respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)]) |
// iOS 7 |
{ |
rotationSnapshotView = [viewForCurrentInterfaceOrientation snapshotViewAfterScreenUpdates:NO]; |
} |
else |
// iOS 6 |
{ |
UIGraphicsBeginImageContextWithOptions(viewForCurrentInterfaceOrientation.bounds.size, YES, viewForCurrentInterfaceOrientation.layer.contentsScale); |
[[viewForCurrentInterfaceOrientation layer] renderInContext:UIGraphicsGetCurrentContext()]; |
UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); |
UIGraphicsEndImageContext(); |
rotationSnapshotView = [[UIImageView alloc] initWithImage:snapshot]; |
} |
rotationSnapshotView.frame = viewForCurrentInterfaceOrientation.frame; |
rotationSnapshotView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; |
[self.view insertSubview:rotationSnapshotView aboveSubview:viewForCurrentInterfaceOrientation]; |
// Save the snapshot view so it can be removed when the animation |
// completes. |
_rotationSnapshotView = rotationSnapshotView; |
// Now that the sanpshot is in place, remove the outgoing view. |
[viewForCurrentInterfaceOrientation removeFromSuperview]; |
} |
if (viewForFinalInterfaceOrientation.superview == nil) |
{ |
[self.view addSubview:viewForFinalInterfaceOrientation]; |
[self.view sendSubviewToBack:viewForFinalInterfaceOrientation]; |
// We're going to fade the incoming view in. |
viewForFinalInterfaceOrientation.alpha = 0.0f; |
} |
} |
//| ---------------------------------------------------------------------------- |
// By the time this method is called |
// a) The system has opened (but not committed) a transaction to |
// animate the rotation. |
// b) The system has resized the root view from the new orientation. |
// c) -viewWillLayoutSubviews has been called. |
// |
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration |
{ |
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
// Snapshot fades out, incoming view fades in. |
// You don't need to setup your own animation transaction here, one |
// has already been setup by the system. |
_rotationSnapshotView.alpha = 0.0f; |
[self viewForInterfaceOrientation:toInterfaceOrientation].alpha = 1.0f; |
} |
//| ---------------------------------------------------------------------------- |
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation |
{ |
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation]; |
[_rotationSnapshotView removeFromSuperview]; |
} |
#pragma mark - |
#pragma mark Utility |
//| ---------------------------------------------------------------------------- |
//! Lazily loads and returns the view of the give \a interfaceOrientation. |
// |
- (UIView*)viewForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation |
{ |
if (UIInterfaceOrientationIsLandscape(interfaceOrientation)) |
{ |
// Lazily load landscapeView if necessary. |
if (self.landscapeView == nil) |
{ |
// Will automatically load LandscapeView~ipad if run on an iPad. |
UINib *landscapeViewXib = [UINib nibWithNibName:@"LandscapeView" bundle:nil]; |
// In the LandscapeView xib the "File's Owner" has been set to |
// ViewController and the landscapeView IBOutlet of the file's owner |
// has been connected to the view defined within the xib. Thus upon |
// instantiation, the unarchived view will be automatically connected |
// to the landscapeView of self. |
[landscapeViewXib instantiateWithOwner:self options:nil]; |
} |
return self.landscapeView; |
} |
else |
{ |
// Lazily load portraitView if necessary. |
if (self.portraitView == nil) |
{ |
// Will automatically load PortraitView~ipad if run on an iPad. |
UINib *portraitViewXib = [UINib nibWithNibName:@"PortraitView" bundle:nil]; |
// In the PortraitView xib the "File's Owner" has been set to |
// ViewController and the portraitView IBOutlet of the file's owner |
// has been connected to the view defined within the xib. Thus upon |
// instantiation, the unarchived view will be automatically connected |
// to the portraitView of self. |
[portraitViewXib instantiateWithOwner:self options:nil]; |
} |
return self.portraitView; |
} |
} |
@end |
Copyright © 2014 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2014-04-02