0%

iOS 对应window旋转事件的响应和传递

iOS 8 横竖屏不响应

自定义appDelegate的window与main.storyboard 对屏幕旋转事件的响应冲突

在开启了横竖屏支持后,状态栏跟随变化但界面没触发旋转,在查找资料后发现,iOS设备的旋转事件会被window响应,通过window的rootViewController控制状态,window 上的view会发生旋转,而项目中ViewController是作为自定义window,需要在TabbarViewController、NavigationViewController、ViewController中传递控制。

但是!!!

在项目新建时,系统会自动创建main.storyboard,它也是携带window,如果没有删除main.storyboard,window响应会冲突,在iOS8上,自定义window的旋转事件不会响应!

解决方案:删除main.storyboard,将自定义window设置为KeyAndVisible,clear 项目,重启Xcode

iOS的App是通过UIWindow这个载体呈现出来的。默认情况下,iOS App对于开发者来说只有一个UIWindow,也就是AppDelegate在applicationDidFinishLaunching里面创建出来的。

横屏及旋转

现在绝大多数的iPhone应用都是竖屏应用,即只支持Portrait模式。但是随着视频、直播的风口到来,在新闻、购物等等APP内都会插入视频播放这一特性,而视频播放需要的全屏播放特性势必要用到横屏,也就意味着会牵扯到旋转。
横屏旋转分为两种,一种是强制性的,一种是随着设备进行旋转的。

大家还记得手机上有旋转锁这一个开关吧,你将旋转锁开启的时候,手机就保持在锁定对应的模式下,无法自动根据你旋转设备而旋转。在这种模式下,如果你需要更改APP界面对应的UIInterfaceOrientation,就必须要么在对应的viewcontroller里面提供实现如下的方法:

1
2
3
4
5
6
7
8
9
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeRight; // 表示支持水平右方向
}

- (BOOL)shouldAutorotate
{
return YES;
}

这样,当你展现到这个页面的时候,就会触发系统检查一下当前页面应该所处的Orientation,从而达到正确的显示效果。
但需要注意的是,如果你的界面是处于一个UINavigationController或者UITabbarController内的话,你就需要从父容器开始,写对应的supportedInterfaceOrientations实现,否则就无法得到正确的效果。

下面介绍监听屏幕旋转方向、视图控制器中旋转方向的设置、屏幕旋转方向下的视图处理以及强制横屏的处理

监听屏幕旋转方向

在处理iOS横竖屏时,经常会和UIDeviceOrientation、UIInterfaceOrientation和UIInterfaceOrientationMask这三个枚举类型打交道,它们从不同角度描述了屏幕旋转方向。

1、UIDeviceOrientation:设备方向

iOS的设备方向是通过iOS的加速计来获取的。

1)iOS定义了以下七种设备方向

1
2
3
4
5
6
7
8
9
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown, // 未知方向,可能是设备(屏幕)斜置
UIDeviceOrientationPortrait, // 设备(屏幕)直立
UIDeviceOrientationPortraitUpsideDown, // 设备(屏幕)直立,上下顛倒
UIDeviceOrientationLandscapeLeft, // 设备(屏幕)向左横置
UIDeviceOrientationLandscapeRight, // 设备(屏幕)向右橫置
UIDeviceOrientationFaceUp, // 设备(屏幕)朝上平躺
UIDeviceOrientationFaceDown // 设备(屏幕)朝下平躺
};

说明:UIDeviceOrientation参考home键方向,如:home方向在右,设备(屏幕)方向向左(UIDeviceOrientationLandscapeLeft)

2)读取设备方向

UIDevice单例代表当前的设备。从这个单例中可以获得的信息设备,如设备方向orientation。
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;

3)监听、处理和移除 设备方向改变的通知

当设备方向变化时候,发出UIDeviceOrientationDidChangeNotification通知;注册监听该通知,可以针对不同的设备方向处理视图展示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
//开启和监听 设备旋转的通知(不开启的话,设备方向一直是UIInterfaceOrientationUnknown)
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleDeviceOrientationChange:)
name:UIDeviceOrientationDidChangeNotification object:nil];
//设备方向改变的处理
- (void)handleDeviceOrientationChange:(NSNotification *)notification{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
switch (ddeviceOrientation) {
case UIDeviceOrientationFaceUp:
NSLog(@"屏幕朝上平躺");
break;
case UIDeviceOrientationFaceDown:
NSLog(@"屏幕朝下平躺");
break;
case UIDeviceOrientationUnknown:
NSLog(@"未知方向");
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"屏幕向左横置");
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"屏幕向右橫置");
break;
case UIDeviceOrientationPortrait:
NSLog(@"屏幕直立");
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(@"屏幕直立,上下顛倒");
break;
default:
NSLog(@"无法辨识");
break;
}
}
//最后在dealloc中移除通知 和结束设备旋转的通知
- (void)dealloc{
//...
[[NSNotificationCenter defaultCenter]removeObserver:self];
[[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications];
}

说明:手机锁定竖屏后,UIDeviceOrientationDidChangeNotification通知就失效了。

2、UIInterfaceOrientation:界面方向

界面方向是反应iOS中界面的方向,它和Home按钮的方向是一致的。

1)iOS定义了以下五种界面方向
1
2
3
4
5
6
7
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown, //未知方向
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait, //界面直立
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown, //界面直立,上下颠倒
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight, //界面朝左
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft //界面朝右
} __TVOS_PROHIBITED;

说明:从定义可知,界面方向和设别方向有对应关系,如界面的竖直方向就是 设备的竖直方向:

UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown

2)读取界面方向

UIInterfaceOrientation和状态栏有关,通过UIApplication的单例调用statusBarOrientation来获取

UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];

3)监听、处理和移除 界面方向改变的通知

当界面方向变化时候,先后发出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//以监听UIApplicationDidChangeStatusBarOrientationNotification通知为例
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(handleStatusBarOrientationChange:)
name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
//界面方向改变的处理
- (void)handleStatusBarOrientationChange: (NSNotification *)notification{
UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
switch (interfaceOrientation) {
case UIInterfaceOrientationUnknown:
NSLog(@"未知方向");
break;
case UIInterfaceOrientationPortrait:
NSLog(@"界面直立");
break;
case UIInterfaceOrientationPortraitUpsideDown:
NSLog(@"界面直立,上下颠倒");
break;
case UIInterfaceOrientationLandscapeLeft:
NSLog(@"界面朝左");
break;
case UIInterfaceOrientationLandscapeRight:
NSLog(@"界面朝右");
break;
default:
break;
}
}
//最后在dealloc中移除通知
- (void)dealloc{
//...
[[NSNotificationCenter defaultCenter]removeObserver:self];
[[UIDevice currentDevice]endGeneratingDeviceOrientationNotifications];
}

说明:手机锁定竖屏后,UIApplicationWillChangeStatusBarOrientationNotification和 UIApplicationDidChangeStatusBarOrientationNotification通知也失效了。

3、UIInterfaceOrientationMask

UIInterfaceOrientationMask是为了集成多种UIInterfaceOrientation而定义的类型,和ViewController相关,一共有7种

1)iOS中的UIInterfaceOrientationMask定义

1
2
3
4
5
6
7
8
9
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),
} __TVOS_PROHIBITED;

2)UIInterfaceOrientationMask的使用

在ViewController可以重写- (UIInterfaceOrientationMask)supportedInterfaceOrientations方法返回类型,来决定UIViewController可以支持哪些界面方向。

1
2
3
4
//支持界面直立
- (UIInterfaceOrientationMask)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}

总结:

UIDeviceOrientation(设备方向)和UIInterfaceOrientation(屏幕方向)是两个不同的概念。前者代表了设备的一种状态,而后者是屏幕为了应对不同的设备状态,做出的用户界面上的响应。在iOS设备旋转时,由UIKit接收到旋转事件,然后通过AppDelegate通知当前程序的UIWindow对象,UIWindow对象通知它的rootViewController,如果该rootViewController支持旋转后的屏幕方向,完成旋转,否则不旋转;弹出的ViewController也是如此处理。