美文网首页iOS项目框架搭建iOS
UIDeviceOrientation与UIinterfaceO

UIDeviceOrientation与UIinterfaceO

作者: 白水灬煮一切 | 来源:发表于2017-11-21 15:35 被阅读90次

    UIDeviceOrientation与UIinterfaceOrientation以及屏幕旋转的方式

    在日常开发过程中,经常会遇到转屏的需求,最近遇到一个转屏相关的bug,顺带着总结下iOS端实现转屏需要做的事情。

    什么是设备方向,什么是视图方向

    首先需要明确两个概念,设备方向(UIDeviceOrientation)和视图方向(UIInterfaceOrientation)

    UIDeviceOrientation:

    不受锁定屏幕方向的影响,通过

    [UIDevice currentDevice].orientation

    客观反映出当前设备所处的方向,与视野中正在展示视图的方向无关,该属性只读,无法修改

    该属性的变化可以通过监听UIDeviceOrientationDidChangeNotification来实时获得设备方向的变化

    UIInterfaceOrientation:

    多数的旋转都需要通过旋转controller来实现。controller的方向也就是我上面提到的视图方向,使用该枚举UIInterfaceOrientation来表达。

    如果想获得当前的视图方向,可以通过以下代码获得

    [UIApplication sharedApplication].statusBarOrientation

    视图方向的改变一定是由于设备方向发生了变化,设备方向的改变也只能是由于物理改变造成的。但是有一个例外,会在后面强制转屏部分详细说明。

    如何让Controller随设备方向旋转

    1. 配置App支持的视图方向

    首先需要配置下当前App能支持的视图方向,所有使用的controller所能支持的视图方向是该配置的子集;

    可以通过以下两种方法进行配置:

    方法1,在Xcode选项中配置:

    在Xcode->工程->General->Deployment Info中对Device Orientation进行配置,注意这里的Device Orientation不是上面所说的设备方向

    方法2,在AppDelegate中通过代码配置:

    在AppDelegate中实现如下方法:

    - (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {

    return UIInterfaceOrientationMaskAll;

    }

    如果同时使用了两种方式,那第二种使用代码的方式会覆盖第一种在Xcode中配置的方式

    2. 配置Controller支持的方向

    代码很简单,表示当前Controller是否支持旋转,支持的方向都有什么,如下:

    //是否支持转屏

    - (BOOL)shouldAutorotate

    {

    return YES;

    }

    //在支持转屏的前提下,返回具体支持的方向

    - (UIInterfaceOrientationMask)supportedInterfaceOrientations

    {

    return UIInterfaceOrientationMaskAll;

    }

    但是这段代码具体要写在哪个viewController中才有效果呢?

    一个viewController是否可以旋转并不是由他自己决定的,而是由rootViewController或者最上层被present上来的viewController来决定的。

    每个App都有一个rootViewController,如果没有通过present的方式推入controller,那么rootViewController支持的方向就决定了视图方向;

    如果有通过present的方式推入controller,那么最近的一次present操作对应的controller就决定了视图方向;

    以上的代码需要放在上面说到的决定了视图方向的controller中

    还有一个回调方法,决定了viewController出现时的视图方向,该回调方法只对present方式进入界面的viewController有效果

    - (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation

    {

    return UIInterfaceOrientationPortrait;

    }

    当然还有一点最重要的,记得要在下拉框中打开设备的转屏锁,要不然怎么转ViewController都不会跟着转的

    关于强制转屏

    在上文中提过,设备方向改变一定是由于物理改变造成的,其实这并不绝对

    苹果的API曾经是允许修改[UIDevice currentDevice].orientation的,后来这个接口被干掉作为私有API了

    但是我们还是可以通过一些其他方式绕过苹果的限制来设置[UIDevice currentDevice].orientation,设备方向一旦改变,再符合上述视图方向改变的条件,视图方向就会被旋转,从而达到强制转屏的效果

    在实际开发过程中也会遇到类似的场景,比如播放器的全屏操作,就是一次强制的视图方向改变

    代码如下:

    NSNumber *orientationTarget = [NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft];

    [[UIDevice currentDevice] setValue:orientationTarget forKey:@"orientation"];

    关于UINavigationController的旋转

    其实道理是一样的,只不过如果present上来一个navigationController的时候,你需要想办法让这个navigationController去实现上述的shouldAutoRotate和supportedInterfaceOrientations方法,可以通过类别或继承重写的方式,这里也就不详述了。

    iOS的横屏(Landscape)与竖屏(Portrait)InterfaceOrientation

    0. 应用级别的配置

    大家(特指有iOS开发经验的人)应该都知道Xcode Project的工程配置General页签中有那么四个图(或者4个checkbox),标识对四种interfaceOrientation的支持。分别为Portrait、PortraitUpsideDown、LandscapeLeft和LandscapeRight。

    对应的,在Xcode Project工程配置的Info页,实际上就是Info.plist中,有对4种Orientation的记录项。

    这两者是一样的。

    1. Window级别的控制

    在iOS6.0之后,UIApplicationDelegate中多了一个方法声明:

    1- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window

    就是对于特定的application和特定的window,我们需要支持哪些interfaceOrientation,这是可以通过实现这个方法定制的。

    返回值是一个无符号整数,实际上是可以使用定义好的枚举值:

    typedefNS_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),

    };

    对于UIApplicationDelegate的这个方法声明,大多数情况下application就是当前的application,而window通常也只有一个。所以基本上通过window对横屏竖屏interfaceOrientation的控制相当于全局的。

    2. Controller层面的控制

    老版本的iOS有这样一个方法:

    1- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientationNS_DEPRECATED_IOS(2_0, 6_0);

    即定制是否可以旋转到特定的interfaceOrientation。

    而在iOS6之后,推出了2个新的方法来完成这个任务:

    1

    2

    - (BOOL)shouldAutorotateNS_AVAILABLE_IOS(6_0);

    - (NSUInteger)supportedInterfaceOrientationsNS_AVAILABLE_IOS(6_0);

    可以看得出来,两个和在一起就是原来任务的完成过程。其中,大概的判断方式是,先执行前者,判断是否可以旋转,如果为YES,则根据是否支持特定的interfaceOrientation再做决断。

    3. 使得特定ViewController坚持特定的interfaceOrientation

    iOS6之后还提供了这样一个方法,可以让你的Controller倔强第坚持某个特定的interfaceOrientation:

    1- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentationNS_AVAILABLE_IOS(6_0);

    这就叫坚持,呵呵!

    当然,使用这个方法是有前提的,就是当前ViewController是通过全屏的Presentation方式展现出来的。

    这里使用的是另外一套枚举量,可以去UIApplication.h中查看定义。

    4. 当前屏幕方向interfaceOrientation的获取

    有3种方式可以获取到“当前interfaceOrientation”:

    controller.interfaceOrientation,获取特定controller的方向

    [[UIApplication sharedApplication] statusBarOrientation] 获取状态条相关的方向

    [[UIDevice currentDevice] orientation] 获取当前设备的方向

    具体区别,可参见StackOverflow的问答:

    http://stackoverflow.com/questions/7968451/different-ways-of-getting-current-interface-orientation

    5. 容器Controller的支持

    上面把interfaceOrientation方向的获取和支持配置都说了,看起来没什么问题了。有没有什么特殊情况?

    当你使用TabbarController和NavigationController按照如上做法使用的时候就会有些头疼。

    办法不是没有,比较通俗的一种就是——继承实现。

    6. UIView的transform属性强制旋转.

    最后一个方法是设置UIView的transform属性来强制旋转.

    见下代码:

    //设置statusBar[[UIApplication sharedApplication] setStatusBarOrientation:orientation];//计算旋转角度float arch;if (orientation == UIInterfaceOrientationLandscapeLeft)    arch = -M_PI_2;else if (orientation == UIInterfaceOrientationLandscapeRight)    arch = M_PI_2;else    arch = 0;//对navigationController.view 进行强制旋转self.navigationController.view.transform = CGAffineTransformMakeRotation(arch);self.navigationController.view.bounds = UIInterfaceOrientationIsLandscape(orientation) ? CGRectMake(0, 0, SCREEN_HEIGHT, SCREEN_WIDTH) : initialBounds;

    相关文章

      网友评论

        本文标题:UIDeviceOrientation与UIinterfaceO

        本文链接:https://www.haomeiwen.com/subject/hbdwvxtx.html