Skip to content

Instantly share code, notes, and snippets.

@nancy-tar
Last active May 14, 2016 17:51
Show Gist options
  • Save nancy-tar/4ea485f15fa8fc3a4cce017538f17048 to your computer and use it in GitHub Desktop.
Save nancy-tar/4ea485f15fa8fc3a4cce017538f17048 to your computer and use it in GitHub Desktop.
Preventing a View From Rotating

#Preventing a View From Rotating ###Technical Q&A QA1890

Q:  My view controller supports auto-rotation but I need to lock the 
orientation of a specific subview. 
How can I prevent a view from rotating?

A: Autorotation is implemented by applying a rotation transform to the application's window when the system determines that the interface must rotate. The window then adjusts its bounds for the new orientation and propagates this change down through the view controller hierarchy via calls to each view controller's and presentation controller's -viewWillTransitionToSize:withTransitionCoordinator: method. Invocations of this method are provided a transition coordinator object containing the delta of the window's current transform and new transform, which can be retrieved by sending a -targetTransform message to the transition coordinator. Your view controller or presentation controller can derive the appropriate inverse transform and apply it to the target view. This nullifies the effect of the window transform on that particular view and gives the appearance that the view has remained fixed in its current orientation as the rest of the interface rotates normally.

Note: In previous iOS versions, it was possible to opt a view out of auto-rotation by adding it as 
a subview of the application's window. Due to changes in the underlying implementation of autorotation, 
this technique no longer works in iOS 8 and later.

Listing 1 Excerpt of a view controller which uses the technique described above to lock the orientation of a view referenced by an instance variable named notRotatingView.

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
 
    self.notRotatingView.center = CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds));
}

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
 
    [coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        CGAffineTransform deltaTransform = coordinator.targetTransform;
        CGFloat deltaAngle = atan2f(deltaTransform.b, deltaTransform.a);
 
        CGFloat currentRotation = [[self.notRotatingView.layer valueForKeyPath:@"transform.rotation.z"] floatValue];
        // Adding a small value to the rotation angle forces the animation to occur in a the desired direction, preventing an issue where the view would appear to rotate 2PI radians during a rotation from LandscapeRight -> LandscapeLeft.
        currentRotation += -1 * deltaAngle + 0.0001;
        [self.notRotatingView.layer setValue:@(currentRotation) forKeyPath:@"transform.rotation.z"];
 
    } completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
        // Integralize the transform to undo the extra 0.0001 added to the rotation angle.
        CGAffineTransform currentTransform = self.notRotatingView.transform;
        currentTransform.a = round(currentTransform.a);
        currentTransform.b = round(currentTransform.b);
        currentTransform.c = round(currentTransform.c);
        currentTransform.d = round(currentTransform.d);
        self.notRotatingView.transform = currentTransform;
    }];
}

#####Considerations

The above technique should only be applied to views which your view controller is directly responsible for the layout of (i.e. descendants of your view controller's view). A view controller should never modify the transform or other geometric properties of its own view, these are managed by the parent view controller or presentation controller. If you need to prevent a view controller from rotating, see Controlling What Interface Orientations Are Supported in the View Controller Programming Guide for iOS.

Autolayout Modifying the transform of a view which has opted into Autolayout is not recommended. If you are using Autolayout, factor the view to which the transform will be applied into a separate xib file with Autolayout disabled. Then at runtime, load the xib file and programmatically add the view to the view hierarchy.

Listing 2 Loading notRotatingView from a xib file. // Connected to the root view in the xib.

@property (nonatomic, strong) IBOutlet UIView *notRotatingView;

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    // Load the notRotatingView from a xib, which has Autolayout disabled.
    [[UINib nibWithNibName:@"NotRotatingView" bundle:[NSBundle bundleForClass:self.class]] instantiateWithOwner:self options:nil];
    [self.view addSubview:self.notRotatingView];
}

link: https://developer.apple.com/library/ios/qa/qa1890/_index.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment