条条大道通罗马,解决同一个问题的手段也是多种多样的。对于《iOS 6及以上控制个别视图旋转案例》中提到的案例,我们是利用系统自带的旋转机制来解决问题的。同样地,我们也可以自己coding解决问题,且最终效果同系统的旋转动画效果是一模一样的。废话不多说,下面来大概讲解一下。
手动控制界面旋转的核心思路就是利用UIView的transform属性,旋转App的根视图。何为根视图?如果你的App的window.rootViewController是UINavigationController,那么根视图就是navigationController.view。为了旋转的效果和系统的一致,我们还需要为它添加一个UIView动画。
接着我们来具体操作一下,首先,建立一个测试工程,工程结构如下图。测试工程的根视图控制器是一个UINavigationController,在这个UINavigationController的栈中有视图控制器a(FirstViewController的一个实例)和控制器b(SecondViewController的一个实例),控制器b通过在控制器a中点击按钮push进入。
在SecondViewController.h文件中添加代码,如下
#import <UIKit/UIKit.h> @interface SecondViewController : UIViewController @property (nonatomic,assign) BOOL isLandscape; - (IBAction)rotateBtnClicked:(id)sender; @end
在SecondViewController.m文件中添加代码,如下
#import "SecondViewController.h" @implementation SecondViewController #pragma mark - view life cycle - (void)viewDidLoad { [super viewDidLoad]; self.title = @"Second"; self.isLandscape = NO; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(changeFrames:) name:UIDeviceOrientationDidChangeNotification object:nil]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - methods - (void)rotateToLandscapeLeft { NSTimeInterval duration = [[UIApplication sharedApplication] statusBarOrientationAnimationDuration]; [UIView animateWithDuration:duration animations:^{ [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeLeft]; self.navigationController.view.transform = CGAffineTransformMakeRotation(- M_PI/2); self.navigationController.view.bounds = CGRectMake(0, 0, [self screenHeight], 320); } completion:^(BOOL finished) { self.isLandscape = YES; }]; } - (void)rotateToLandscapeRight { NSTimeInterval duration = [[UIApplication sharedApplication] statusBarOrientationAnimationDuration]; [UIView animateWithDuration:duration animations:^{ [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight]; self.navigationController.view.transform = CGAffineTransformMakeRotation(M_PI/2); self.navigationController.view.bounds = CGRectMake(0, 0, [self screenHeight], 320); } completion:^(BOOL finished) { self.isLandscape = YES; }]; } - (void)rotateToPortrait { NSTimeInterval duration = [[UIApplication sharedApplication] statusBarOrientationAnimationDuration]; [UIView animateWithDuration:duration animations:^{ [[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait]; self.navigationController.view.transform = CGAffineTransformMakeRotation(0); self.navigationController.view.bounds = CGRectMake(0, 0, 320, [self screenHeight]); } completion:^(BOOL finished) { self.isLandscape = NO; }]; } - (CGFloat)screenHeight { if (iPhone5) { return 568.0; } else { return 480.0; } } #pragma mark - actions - (IBAction)rotateBtnClicked:(id)sender { if (!self.isLandscape) { [self rotateToLandscapeRight]; } else { [self rotateToPortrait]; } } #pragma mark - handle notification - (void)changeFrames:(NSNotification *)notification { UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation]; if (deviceOrientation == UIDeviceOrientationLandscapeRight) { [self rotateToLandscapeLeft]; } else if (deviceOrientation == UIDeviceOrientationLandscapeLeft) { [self rotateToLandscapeRight]; } else if (deviceOrientation == UIDeviceOrientationPortrait){ [self rotateToPortrait]; } } @end
其中,rotateToLandscapeLeft/rotateToLandscapeRight/rotateToPortrait是视图旋转的核心方法。我们在viewDidLoad方法中注册了一个事件通知,当设备的物理旋转方向发生改变时,我们将调用方法changeFrames:来对视图进行相应的旋转,也可以通过点击某个按钮来旋转界面。可以看到,测试项目中还有一个UINavigationController的类别。类别中添加
#import "UINavigationController+Ext.h" @implementation UINavigationController (Ext) - (BOOL)shouldAutorotate { return NO; } @end
为什么要添加UINavigationController类别?因为旋转的时候我们需要同时去旋转statusBar(状态栏),要旋转statusBar必须在shouldAutorotate方法中返回NO。
运行测试项目并旋转设备,能看到我们已经成功手动控制界面旋转,且旋转的动画效果与系统默认的旋转效果是一模一样的。和之前那篇文章中提到的解决方法相比,有了更高的自由度,开发者能够随性控制界面的旋转。
测试工程:下载
iOS手动控制界面旋转