一、两种orientation

了解屏幕旋转首先需要区分两种orientation

1、device orientation

设备的物理方向,由类型UIDeviceOrientation表示,当前设备方向获取方式:

1
[UIDevice currentDevice].orientation

该属性的值一般是与当前设备方向保持一致的,但须注意以下几点:

①文档中对该属性的注释:

1
@property(nonatomic,readonly) UIDeviceOrientation orientation;       // return current device orientation.  this will return UIDeviceOrientationUnknown unless device orientation notifications are being generated.

所以更推荐下面这种用法:

1
2
3
4
5
6
7
if (![UIDevice currentDevice].generatesDeviceOrientationNotifications) {
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
}
NSLog(@"%d",[UIDevice currentDevice].orientation); [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications];

②系统横竖屏开关关闭时

如果关闭了系统的横竖屏切换开关,即系统层级只允许竖屏时,再通过上述方式获取到的设备方向将永远是UIDeviceOrientationUnknown。可以通过Core Motion中的CMMotionManager来获取当前设备方向。

2、interface orientation

界面显示的方向,由类型UIInterfaceOrientation表示。当前界面显示方向有以下两种方式获取:

1
2
NSLog(@"%d",[UIApplication sharedApplication].statusBarOrientation);
NSLog(@"%d",viewController.interfaceOrientation);

即可以通过系统statusBar的方向或者viewController的方向来获取当前界面方向。

3、二者区别

通过UIDevice获取到的设备方向在手机旋转时是实时的,通过UIApplication的statusBar或者viewController获取到的界面方向在下述方法:

1
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:

调用以后才会被更改成最新的值。

二、相关枚举定义

1、UIDeviceOrientation:

1
2
3
4
5
6
7
8
9
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
UIDeviceOrientationUnknown,
UIDeviceOrientationPortrait, // Device oriented vertically, home button on the bottom
UIDeviceOrientationPortraitUpsideDown, // Device oriented vertically, home button on the top
UIDeviceOrientationLandscapeLeft, // Device oriented horizontally, home button on the right
UIDeviceOrientationLandscapeRight, // Device oriented horizontally, home button on the left
UIDeviceOrientationFaceUp, // Device oriented flat, face up
UIDeviceOrientationFaceDown // Device oriented flat, face down
};

2、UIInterfaceOrientation:

1
2
3
4
5
6
7
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
UIInterfaceOrientationUnknown = UIDeviceOrientationUnknown,
UIInterfaceOrientationPortrait = UIDeviceOrientationPortrait,
UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,
UIInterfaceOrientationLandscapeRight = UIDeviceOrientationLandscapeLeft
};

从宏定义可知,device方向比interface多了两个定义:UIDeviceOrientationFaceUpUIDeviceOrientationFaceDown,分别表示手机水平放置,屏幕向上和屏幕向下。

三、相关方法

1、iOS5中控制屏幕旋转的方法:

1
2
// Applications should use supportedInterfaceOrientations and/or shouldAutorotate..
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation NS_DEPRECATED_IOS(2_0, 6_0);

如果打算支持toInterfaceOrientation对应的方向就返回YES,否则返回NO。

2、iOS6中控制屏幕旋转相关方法:

1
2
3
4
5
// New Autorotation support.
- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0);
- (NSUInteger)supportedInterfaceOrientations NS_AVAILABLE_IOS(6_0);
// Returns interface orientation masks.
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0);

第一个方法决定是否支持多方向旋转屏,如果返回NO则后面的两个方法都不会再被调用,而且只会支持默认的UIInterfaceOrientationMaskPortrait方向;

第二个方法直接返回支持的旋转方向,该方法在iPad上的默认返回值是UIInterfaceOrientationMaskAll,iPhone上的默认返回值是UIInterfaceOrientationMaskAllButUpsideDown,详情见官方Q&A文档

第三个方法返回最优先显示的屏幕方向,比如同时支持Portrait和Landscape方向,但想优先显示Landscape方向,那软件启动的时候就会先显示Landscape,在手机切换旋转方向的时候仍然可以在Portrait和Landscape之间切换;

3、attemptRotationToDeviceOrientation方法

从iOS5开始有了这个新方法:

1
2
3
// call this method when your return value from shouldAutorotateToInterfaceOrientation: changes
// if the current interface orientation does not match the current device orientation, a rotation may occur provided all relevant view controllers now return YES from shouldAutorotateToInterfaceOrientation:
+ (void)attemptRotationToDeviceOrientation NS_AVAILABLE_IOS(5_0);

该方法的使用场景是interface orientation和device orientation不一致,但希望通过重新指定interface orientation的值,立即实现二者一致;如果这时只是更改了支持的interface orientation的值,没有调用attemptRotationToDeviceOrientation,那么下次device orientation变化的时候才会实现二者一致,关键点在于能不能立即实现。

举个例子:

假设当前的interface orientation只支持Portrait,如果device orientation变成Landscape,那么interface orientation仍然显示Portrait;

如果这时我们希望interface orientation也变成和device orientation一致的Landscape,以iOS6为例,需要先将supportedInterfaceOrientations的返回值改成Landscape,然后调用attemptRotationToDeviceOrientation方法,系统会重新询问支持的interface orientation,已达到立即更改当前interface orientation的目的。

四、如何决定interface orientation

1、全局控制

Info.plist文件中,有一个Supported interface orientations,可以配置整个应用的屏幕方向,此处为全局控制。

2、UIWindow

iOS6的UIApplicationDelegate提供了下述方法,能够指定 UIWindow 中的界面的屏幕方向:

1
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window  NS_AVAILABLE_IOS(6_0);

该方法默认值为Info.plist中配置的Supported interface orientations项的值。

iOS中通常只有一个window,所以此处的控制也可以视为全局控制。

3、controller

只有以下两种情况:

  • 当前controller是window的rootViewController
  • 当前controller是modal模式的

时,orientations相关方法才会起作用(才会被调用),当前controller及其所有的childViewController都在此作用范围内。

4、最终支持的屏幕方向

前面所述的3种控制规则的交集就是一个controller的最终支持的方向;

如果最终的交集为空,在iOS6以后会抛出UIApplicationInvalidInterfaceOrientationException崩溃异常。

四、强制屏幕旋转

如果interface和device方向不一样,想强制将interface旋转成device的方向,可以通过attemptRotationToDeviceOrientation实现,但是如果想将interface强制旋转成任一指定方向,该方式就无能为力了。

不过聪明的开发者们总能想到解决方式:

1、私有方法

1
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationPortrait];

但是现在苹果已经将该方法私有化了,越狱开发的同学可以试试,或者自己想法子骗过苹果审核吧。

2、旋转view的transform

也可以通过旋转view的transform属性达到强制旋转屏幕方向的目的,但个人感觉这不是靠谱的思路,可能会带来某些诡异的问题。

3、主动触发orientation机制

要是能主动触发系统的orientation机制,调用orientation相关方法,使新设置的orientation值起作用就好了。这样只要提前设置好想要支持的orientation,然后主动触发orientation机制,便能实现将interface orientation旋转至任意方向的目的。

万能的stackoverflow上提供了一种主动触发的方式:

在iOS4和iOS6以后:

1
2
3
4
UIViewController *vc = [[UIViewController alloc]init];
[self presentModalViewController:vc animated:NO];
[self dismissModalViewControllerAnimated:NO];
[vc release];

iOS5中:

1
2
3
4
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];

这种方式会触发UIKit重新调用controller的orientation相关方法,以达到在device方向不变的情况下改变interface方向的目的。

虽然不优雅,但却能解决问题,凑合吧。。

PS:

话说iOS8中的屏幕旋转相关方法又变化了,表示适配起来很蛋疼。。。

五、参考文档

转自:http://foggry.com/blog/2014/08/08/ping-mu-xuan-zhuan-xue-xi-bi-ji/

iOS屏幕旋转 浅析的更多相关文章

  1. 【转】IOS屏幕旋转与View的transform属性之间的关系,比较底层

    iTouch,iPhone,iPad设置都是支持旋转的,如果我们的程序能够根据不同的方向做出不同的布局,体验会更好. 如何设置程序支持旋转呢,通常我们会在程序的info.plist中进行设置Suppo ...

  2. IOS屏幕旋转思路和实践

    这段时间同事在做一个直播项目,项目有个需求:一个界面需要手动设置屏幕的方向,设置好之后方向不能变化.完成这个需求花了特别大的精力,归因是网上关于屏幕旋转的知识比较凌乱,解决问题花费不少时间,最后决定把 ...

  3. ios 屏幕旋转的问题

    在ios6之前我们旋转屏幕只需要实现shouldAutorotateToInterfaceOrientation就行了 - (BOOL)shouldAutorotateToInterfaceOrien ...

  4. iOS 屏幕旋转 nav+tabbar+present(网页) 2016

    如题,最近一个app架构为 nav + tabbar ,需求是 在点击tabbar中的一个菜单项时,弹出网页,该网页需要横屏显示,其他页面不变  都保持竖屏. XCode Version 7.2.1 ...

  5. iOS屏幕旋转

    三种方法 需求:全局主要是竖屏 个别界面需要横屏

  6. 【转】IOS设备旋转的内部处理流程以及一些【优化建议】

    加速计是整个IOS屏幕旋转的基础,依赖加速计,设备才可以判断出当前的设备方向,IOS系统共定义了以下七种设备方向: typedef NS_ENUM(NSInteger, UIDeviceOrienta ...

  7. iOS实现屏幕旋转

    iOS实现屏幕旋转有两种方式 1. 应用本身支持 2. 手动旋转UIView (这种方式我没找到旋转 系统控件的方法 如:键盘.导航.UIAlertView) 如果你只是要把竖屏的播放器,做成支持横屏 ...

  8. ios实现屏幕旋转的方法

    1.屏蔽AppDelegate下面的屏幕旋转方法 #pragma mark - 屏幕旋转的 //- (UIInterfaceOrientationMask)application:(UIApplica ...

  9. 监听iOS检测屏幕旋转状态,不需开启屏幕旋转-b

    -(void)rotation_icon:(float)n { UIButton *history_btn= [self.view viewWithTag:<#(NSInteger)#>] ...

随机推荐

  1. SSL读书笔记

    摘要: 第一次写博客,为读书笔记,参考书目如下: <HTTP权威指南> <图解HTTP> <大型分布式网站架构设计与实践> 作者:陈康贤 一. HTTP+SSL=H ...

  2. P2P直连?经服务器中转?

    当同一个系统的两个客户端A.B相互发送消息给对方时,如果它们之间存在P2P通道,那么消息传送的路径就有两种:直接经P2P通道传送.或者经服务器中转.如下图所示: 通常就一般应用而言,如果P2P通道能够 ...

  3. OMCS开发手册(02) -- 多媒体连接器

    OMCS开发手册(01) -- 多媒体设备管理器 一文,我们从Owner的角度详细描述了多媒体设备管理器的使用,本文我们将站在Guest的角度,描述OMCS中另一类组件/控件:多媒体连接器.多媒体连接 ...

  4. 朋友遇到过的t厂面试题

    朋友遇到过的t面试题 leetcode160 找链表交点 leetcode206 反转链表

  5. chapter11_3 字符串缓冲

    逐行地读取一个文件,典型的代码是: local buff= " " for line in io.lines() do buff = buff .. line .. "\ ...

  6. 获取android源码中遇到的问题

    最近项目相当紧张,回家之后还需要继续研究android源码. 从网上找了一些资料,下载过程中还是遇到了很多问题.这里罗列一下: 1. 下载Repo过程中提示证书问题: curl https://dl- ...

  7. mysql多表链接查询

    select area from areas where cityid=(select cityid from cities where city like '杭州%') and id!=(selec ...

  8. redis的主从复制与哨兵

    主从复制的关键字是slaveof,有三种方法可以让一个redis数据库变成另一个redis数据库的从数据库: 1.修改redis的配置文件,添加#slaveof <masterip> &l ...

  9. ios沙盒查找图片展示

    iOS如何找到自己的沙盒 在ios开发我们会用到沙盒,由于自己对沙盒理解的不够,所以找不到沙盒文件在哪里,当然要知道路径了 例如我的路径 NSString* cachepath = [NSHomeDi ...

  10. 用Py2exe打包Python脚本简单介绍

    一.简述      Py2exe,从这个名字上就可以理解,把Python脚本转换为windows平台上面可以运行的可执行程序(*.exe)的工具.经过转换后,你可以不 用安装Python的执行环境就可 ...