iOS横竖屏总结

使用Auto Layout来进行布局就不用自己去监听横竖屏事件了,只需要绘制多套布局即可。但是项目有很多页面是自己手动计算的,于是只有想办法再旋转屏幕时重新布局。

相关枚举

屏幕方向有3个相关枚举,界面方向UIInterfaceOrientation,设备方向UIDeviceOrientation,支持旋转方向UIInterfaceOrientationMask

注意UIInterfaceOrientation与UIDeviceOrientation左右方向是相反的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
};

typedef NS_OPTIONS(NSUInteger, UIInterfaceOrientationMask) {
UIInterfaceOrientationMaskPortrait = (1 << UIInterfaceOrientationPortrait),
UIInterfaceOrientationMaskLandscapeLeft = (1 << UIInterfaceOrientationLandscapeLeft),
UIInterfaceOrientationMaskLandscapeRight = (1 << UIInterfaceOrientationLandscapeRight),
UIInterfaceOrientationMaskPortraitUpsideDown = (1 << UIInterfaceOrientationPortraitUpsideDown),
UIInterfaceOrientationMaskLandscape = (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
UIInterfaceOrientationMaskAll = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskPortraitUpsideDown),
UIInterfaceOrientationMaskAllButUpsideDown = (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight),
};

横竖屏控制

控制界面横竖屏切换有3个重要的点,最终结果以这三个地方的值取交集。

1.info.plist全局控制

可以在General->Deplyment Info界面上勾选

15320065885480

info.plist文件中配置也是一样的,两边会同步变更

15320512384395

2.AppDelegate中根据不同Window控制

1
2
3
4
// AppDelegate
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskPortrait;
}

3.在ViewController中控制当前页面

1
2
3
4
5
6
7
8
// UIViewController
- (BOOL)shouldAutorotate {
return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeLeft;
}

需要注意的是,交集不能为空,否则会导致crash

*** Terminating app due to uncaught exception ‘UIApplicationInvalidInterfaceOrientation’, reason: ‘Supported orientations has no common orientation with the application, and [ViewController
shouldAutorotate] is returning YES’

15320548897266

旋转事件监听

旋转事件传递过程

1
2
3
4
5
6
7
op0=>operation: __CFRunLoopDoSources0
op1=>operation: UIDevice
op2=>operation: UIWindow
op3=>operation: UIViewController
op4=>operation: UIView

op1->op2->op3->op4

屏幕旋转相关事件

viewWillTransitionToSize:withTransitionCoordinator:

  • ViewController被父容器变更size时调用(例如window旋转时调用root view controller的该方法)
  • 如果重载该方法,需要调用super传递事件给子ViewController
  • 这个方法是最关键的,可以在该方法中对界面进行重新布局
1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
// coordinator用来处理转换动画
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
// 开始旋转
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
{
// 旋转结束
}];
// 记得调用super
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
}

UIApplicationWillChangeStatusBarOrientationNotification

  • 状态栏将要旋转,这个时候取view的frame还是旋转之前的
  • NSNotification中用key UIApplicationStatusBarOrientationUserInfoKey可以取到将要旋转到的方向。

UIApplicationDidChangeStatusBarOrientationNotification

  • 状态栏已经旋转,这个时候取view的frame是旋转之后的
  • NSNotification中用key UIApplicationStatusBarOrientationUserInfoKey可以取到旋转之前的方向。

UIDeviceOrientationDidChangeNotification

  • 设备方向变更,在收到通知时取view的frame是旋转之后的。
  • 在手机上将旋转屏幕锁定之后,设备方向变更之后收不到该通知
  • 在代码里面限制设备旋转方向,设备方向变更后依然能收到该通知

调用顺序如下

1
2
3
4
5
6
7
8
9
10
st=>start: 旋转屏幕
e=>end: 结束
op1=>operation: viewWillTransitionToSize:withTransitionCoordinator:
op2=>operation: UIApplicationWillChangeStatusBarOrientationNotification
op3=>operation: UIApplicationDidChangeStatusBarOrientationNotification
op4=>operation: viewWillLayoutSubviews
op5=>operation: viewDidLayoutSubviews
op6=>operation: UIDeviceOrientationDidChangeNotification

st->op1->op2->op3->op4->op5->op6->e

自定义Window的旋转事件

如果想要在自定义Window的子View收到屏幕旋转通知,要设置UIWindow的rootViewController,然后把所有子view都加到rootViewController,系统会处理横竖屏事件。这里我还遇到一个坑记一个实现UIWindow子类的小坑


推荐阅读

https://satanwoo.github.io/2016/09/17/uiwindow-iOS/
iOS屏幕旋转知识点以及实现
iOS 屏幕旋转的那些事(一)
浅谈iOS的多Window处理