前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS横竖屏切换

iOS横竖屏切换

作者头像
落影
发布2022-09-28 15:42:46
2.4K0
发布2022-09-28 15:42:46
举报
文章被收录于专栏:落影的专栏落影的专栏

基础概念

UIDeviceOrientation

UIDeviceOrientation,表示设备朝向,可以通过[UIDevice currentDevice] orientation]获取,取值有:

代码语言:javascript
复制
typedef NS_ENUM(NSInteger, UIDeviceOrientation) {
    UIDeviceOrientationUnknown,             // 未知,启动时会出现
    UIDeviceOrientationPortrait,            // 竖屏,home键在底部
    UIDeviceOrientationPortraitUpsideDown,  // 倒立,home键在顶部
    UIDeviceOrientationLandscapeLeft,       // 左横屏,home键在右边
    UIDeviceOrientationLandscapeRight,      // 右横屏,home键在左边
    UIDeviceOrientationFaceUp,              // 屏幕朝上
    UIDeviceOrientationFaceDown             // 屏幕朝下
}

UIInterfaceOrientation

UIInterfaceOrientation,表示页面内容朝向,注意UIInterfaceOrientation和UIDeviceOrientation的关系,其中UIInterfaceOrientationLandscapeLeft = UIDeviceOrientationLandscapeRight,这是因为: This is because rotating the device to the left requires rotating the content to the right. 不用特别细究两者之间关系,我们只需要根据需要设置好UIInterfaceOrientation即可,通过 [UIApplication shareApplication] statusBarOrientation]可以获取当前状态栏朝向。

代码语言:javascript
复制
typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

UIInterfaceOrientationMask

UIInterfaceOrientationMask,是由页面内容朝向的二进制偏移组成,用来更方便描述某个界面支持的朝向。比如说下面的UIInterfaceOrientationMaskLandscape,其实就是由MaskLandscapeLeft和MaskLandscapeRight组成,这样可以方便描述设备支持两个横屏方向。

代码语言:javascript
复制
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),
};

UIViewController相关

UIViewController关于横竖屏的三个方法:

  1. shouldAutorotate,页面是否允许自动旋转,被弃用api:-shouldAutorotateToInterfaceOrientation的取代者;默认值为YES,表示当前界面允许跟随设备旋转而自动旋转;
  2. supportedInterfaceOrientations,该界面支持的界面朝向,可以返回四个朝向的任意组合,iPad默认值是四个朝向都支持,iPhone默认值是除了UpsideDown的三个朝向。这个方法回调的前提是shouldAutorotate=YES。
  3. preferredInterfaceOrientationForPresentation,该界面被present出来的界面朝向,可以返回四个朝向的任意组合。如果没有返回,则present时和原来界面保持一致。

AppDelegate相关

AppDelegate的supportedInterfaceOrientationsForWindow方法,根据需要返回当前window是否支持横屏。

代码语言:javascript
复制
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window;

工程配置相关

在xcode的工程设置的General可以配置iPhone和iPad的页面朝向支持。

横竖屏切换实例

竖屏界面如何present横屏界面

竖屏present横屏是很普遍的场景,比如说视频播放场景的全屏切换,就可以在当前竖屏的界面present一个横屏播放界面的方式,实现横竖屏切换。具体的操作步骤只需要两步:

1,设置modalPresentationStyle为UIModalPresentationFullScreen; 2、preferredInterfaceOrientationForPresentation方法,返回UIInterfaceOrientationLandscapeRight;

比如说下面的LandscapeViewController界面:

代码语言:javascript
复制
// 点击时设置
- (void)onClick {
    LandscapeViewController *landVC = [[LandscapeViewController alloc] init];
    landVC.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:landVC animated:YES completion:nil];
}

// LandscapeViewController内部代码
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

思考🤔:

1、如果是横屏转竖屏呢? 2、如果想要自定义旋转效果实现呢?(UIViewControllerAnimatedTransitioning协议)

竖屏界面如何push横屏界面

比如说这样的场景:App的rootVC是navigationVC,导航栈内先有一个竖屏界面,现在想要push一个横屏界面LandscapeViewController。

一个简单的方式如下:

代码语言:javascript
复制
// appdelegate实现
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    if ([self.navigationVC.topViewController isKindOfClass:LandscapeViewController.class]) {
        return UIInterfaceOrientationMaskLandscapeRight;
    }
    else {
        return UIInterfaceOrientationMaskPortrait;
    }
}
// LandscapeViewController内部实现
- (void)viewDidLoad {
    NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[UIDevice instanceMethodSignatureForSelector:@selector(setOrientation:)]];
    invocation.selector = NSSelectorFromString(@"setOrientation:");
    invocation.target = [UIDevice currentDevice];
    int initOrientation = UIDeviceOrientationLandscapeRight;
    [invocation setArgument:&initOrientation atIndex:2];
    [invocation invoke];
}

思考🤔:

1、这里为什么没有用到UIViewController的三个方法? 2、在viewDidLoad调用的旋转方法是什么意思?

横屏竖切换机制分析

前面的实例介绍了如何支持切换,但是也产生一些疑问: 工程配置文件也没有设置横屏,为什么后面就能支持横屏? 工程配置、AppDelegate、UIViewController这三者,在横竖屏切换过程的关系是什么? 自动旋转和手动旋转有什么区别? .... 仅仅知道切换适配代码,是无法形成横竖屏切换理解,也就很难回答上述的问题。 由于没有找到解释横竖屏切换机制的官方文档,以下根据自己的经验对这个切换的机制进行分析。

系统如何知道App对界面朝向的支持

这里分两种情况,App启动前和App运行时。

App启动前 在App启动前进程还未加载,代码无法运行,系统肯定无法通过AppDelegate或者UIViewController这种代码的方式获取横竖屏的配置。所以在这种情况下,工程配置中的plist描述App对屏幕的适配,就可以很好帮助系统识别应该以什么样的朝向启动App。 所以在plist中增加横屏的支持,好处是开屏能够支持横屏,这样界面展示更加顺滑;坏处也是开屏支持了横屏,导致开屏为横屏启动的时候,UIScreen的mainScreen是横屏的大小,但很多业务逻辑代码都会以[UIScreen mainScreen]去取屏幕宽度和高度,所以很容易取到错误的值。

App运行时 当App进程加载完成,此时系统可以通过运行时询问的方式,来动态获取不同时机的界面朝向。 此时AppDelegate控制的是UIWindow层面的朝向,UIViewController控制的是VC层面的朝向。需要注意的是,当我们返回UIViewController的朝向时,还要考虑父容器的朝向。通常一个App的界面层级是UIWindow=>RootViewController(容器vc)=>UIViewController(界面vc)。假如只在UIWindow返回界面朝向也是允许的,就如同上面的实例分析中的push横屏。

不同界面的朝向控制

还是假设UIWindow=>RootViewController(容器vc)=>UIViewController(界面vc)的层级,且当前ViewController是竖屏vc,现在需要push一个横屏界面LandscapeViewController。 在每次界面切换的时候,系统都会回调确认新的界面朝向,最终结果为UIWindow朝向、容器vc朝向、界面vc朝向三者的“与”值。那么假如这个值冲突了呢? 假如supportedInterfaceOrientationsForWindow一直返回的竖屏,那么后面VC设置横屏不会生效; 类似,假如UIWindow设置的是横屏,那么后面VC设置竖屏也不会生效; 如果在界面切换的过程中发现返回的朝向值未确定,系统更倾向于保持当前朝向不变,并且可能会遇到以下的crash。

代码语言:javascript
复制
Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', reason: 'Supported orientations has no common orientation with the application, and [LandscapeViewController shouldAutorotate] is returning YES'

这个原则同样适用于当返回多个结果,比如说当前界面是竖屏,然后UIWindow和ViewController的界面朝向都支持横屏和竖屏都支持,那么此时会保持竖屏。

一种比较常用的设计:

代码语言:javascript
复制
// AppDelegate
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
    return self.navigationVC.topViewController.supportedInterfaceOrientations;
}

// NavigationController
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    return self.topViewController.supportedInterfaceOrientations;
}

// LandscapeViewController
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
    return UIInterfaceOrientationLandscapeRight;
}

自动旋转和手动旋转

自动旋转指的是我们旋转物理设备时,系统会触发界面的旋转。当我们从一个竖屏界面push一个横屏界面时,即使横屏界面设置了shouldAutorotate=YES,这个界面也不会变成横屏,但是拿起来设备左右翻转的时候,会发现随着设备旋转,界面也从横屏变成了竖屏。这就是自动旋转。 手动旋转指的是手动触发旋转,根据经验发现有两个api,UIViewController的+attemptRotationToDeviceOrientation,还有UIDevice的setOrientation:可以调整界面朝向。前者是将界面朝向对齐设备朝向,是标准api;后者是调整设备朝向,是私有api。 假如我们在很多个竖屏界面中,需要强制横屏某一个界面,如果是子界面可以使用present的方式,如果是push那么就必须要用到这个私有api。

注意事项

其他横竖屏适配方式

1、视图适配:通过transform修改layer从而在视图上实现横屏,但是此时屏幕宽度、状态栏、安全距离等都保留竖屏状态,这种方式仅仅适用于横屏弹窗等部分场景; 2、新建Window:由于App的适配是UIWindow为单位,那么理论上是可以新建一个UIWindow来横屏的界面;

横竖屏切换通知

NSNotification通知

代码语言:javascript
复制
[[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {
        NSLog(@"NSNotification:%@, orientation:%d", note.userInfo, [(UIDevice *)note.object orientation]);
    }];

UIViewController回调

代码语言:javascript
复制
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0));
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-09-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基础概念
    • UIDeviceOrientation
      • UIInterfaceOrientation
        • UIInterfaceOrientationMask
          • UIViewController相关
            • AppDelegate相关
              • 工程配置相关
              • 横竖屏切换实例
                • 竖屏界面如何present横屏界面
                  • 竖屏界面如何push横屏界面
                  • 横屏竖切换机制分析
                    • 系统如何知道App对界面朝向的支持
                      • 不同界面的朝向控制
                        • 自动旋转和手动旋转
                        • 注意事项
                          • 其他横竖屏适配方式
                            • 横竖屏切换通知
                            相关产品与服务
                            容器服务
                            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                            领券
                            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档