iOS横竖屏切换

news2025/2/13 10:09:24

  • 基础概念
    • UIDeviceOrientation
    • UIInterfaceOrientation
    • UIInterfaceOrientationMask
    • UIViewController相关
    • AppDelegate相关
    • 工程配置相关
  • 横竖屏切换实例
    • 竖屏界面如何present横屏界面
    • 竖屏界面如何push横屏界面
    • 横屏竖切换机制分析
    • 系统如何知道App对界面朝向的支持
    • 不同界面的朝向控制
    • 自动旋转和手动旋转
  • 注意事项
    • 其他横竖屏适配方式
    • 横竖屏切换通知

基础概念

UIDeviceOrientation

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

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]可以获取当前状态栏朝向。

typedef NS_ENUM(NSInteger, UIInterfaceOrientation) {
    UIInterfaceOrientationUnknown            = UIDeviceOrientationUnknown,
    UIInterfaceOrientationPortrait           = UIDeviceOrientationPortrait,
    UIInterfaceOrientationPortraitUpsideDown = UIDeviceOrientationPortraitUpsideDown,
    UIInterfaceOrientationLandscapeLeft      = UIDeviceOrientationLandscapeRight,
    UIInterfaceOrientationLandscapeRight     = UIDeviceOrientationLandscapeLeft
};

UIInterfaceOrientationMask

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

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是否支持横屏。

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

工程配置相关

在xcode的工程设置的General可以配置iPhone和iPad的页面朝向支持。
在这里插入图片描述

横竖屏切换实例

竖屏界面如何present横屏界面

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

  1. 设置modalPresentationStyle为UIModalPresentationFullScreen;
  2. preferredInterfaceOrientationForPresentation方法,返回UIInterfaceOrientationLandscapeRight;
    比如说下面的LandscapeViewController界面:
// 点击时设置
- (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。

一个简单的方式如下:

// 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。

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的界面朝向都支持横屏和竖屏都支持,那么此时会保持竖屏。

一种比较常用的设计:

// 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通知

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

UIViewController回调

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator API_AVAILABLE(ios(8.0));

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/639586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt学习06:QPainter绘画

文章首发于我的个人博客&#xff1a;欢迎大佬们来逛逛 Qt学习06&#xff1a;QPainter绘画 Qt绘图 Paint System Qt的绘制系统支持在屏幕和打印设备上使用相同的API进行绘制&#xff0c;主要基于QPainter、QPaintDevice和QPaintEngine类。 QPainter用于执行绘图操作&#xff…

JAVA基础 - SPI机制使用详解(三)

简述 SPI&#xff08;Service Provider Interface的缩写&#xff09; 意思是&#xff1a;“服务提供者的接口”&#xff0c;专门提供给服务提供者或者扩展框架功能的开发者去使用的接口。SPI 将服务接口和服务实现分离开来&#xff0c;将服务调用方和服务实现方进行解耦&#…

Rocketmq面试(四)RocketMQ 的推模式和拉模式有什么区别?

一、PUSH模式 public class Consumer {public static void main(String[] args) throws InterruptedException, MQClientException {// 初始化consumer&#xff0c;并设置consumer group nameDefaultMQPushConsumer consumer new DefaultMQPushConsumer("please_rename_…

基于STM32的重力感应售货机系统设计

一、项目介绍 随着智能物联网技术的不断发展&#xff0c;人们的生活方式和消费习惯也正在发生改变。如今越来越多的人习惯于在线购物、自助购物等新型消费模式&#xff0c;因此智能零售自助柜应运而生。 本项目设计开发一款基于STM32主控芯片的智能零售自助柜&#xff0c;通过…

哪吒汽车,莫做“普信男”

作者 | 魏启扬 来源 | 洞见新研社 今年初&#xff0c;哪吒汽车创始人方运舟和张勇联合发表新年致辞&#xff0c;文末总结说 “2023-2025年&#xff0c;必将是一场艰难的挑战&#xff0c;也是哪吒汽车的生死存亡之战。” 哪吒汽车或许过于敏感了&#xff0c;就今年以来的市场表…

Tensorflow两步安装(超简单)

一、查看python版本&#xff0c;下载对应tensorflow文件 1.Anaconda已安装&#xff0c;找到Anaconda3文件夹&#xff0c;双击打开anaconda prompt&#xff0c;输入python&#xff0c;查看python版本 可以看到我的版本是3.9的 2.进入下面的网站&#xff0c;选择你需要的cpu或g…

【appium】appium自动化入门之API(下)——两万字API长文,建议收藏

目录 Appium API 前言 1.contexts &#xff08;返回当前会话中的上下文&#xff0c;使用后可以识别 H5 页面的控件&#xff09; 2.current_context &#xff08;返回当前会话的当前上下文 &#xff09; 3. context &#xff08;返回当前会话的当前上下文&#xff09; 4.find_e…

Django-搭建sysinfo获取系统信息

文章目录 前言一、项目搭建二、主机信息监控三、Celery定时任务和异步任务 前言 本篇基于&#xff1a;https://github.com/hypersport/sysinfo#readme 使用Django&#xff0c;搭建sysinfo&#xff0c;Linux中,sysinfo是用来获取系统相关信息的结构体 一、项目搭建 &#xff0…

CV方向如何找到适合自己的研究创新点?

做CV的论文创新的一些思路与方向。分别是无事生非&#xff0c;后浪推前浪&#xff0c;推陈出新&#xff0c;出奇制胜。 无事生非 在原始的数据集上加一些噪声&#xff0c;例如随机遮挡&#xff0c;或者调整饱和度亮度什么的&#xff0c;主要是根据具体的任务来增加噪声或扰动&a…

大模型LLM-微调经验分享总结

模型越大对显卡的要求越高&#xff0c;目前主流对大模型进行微调方法有三种&#xff1a;Freeze方法、P-Tuning方法和Lora方法。笔者也通过这三种方法&#xff0c;在信息抽取任务上&#xff0c;对ChatGLM-6B大模型进行模型微调。liucongg/ChatGLM-Finetuning: 基于ChatGLM-6B模型…

I/O设备详解

目录 一. 什么是IO设备 二. IO设备分类 2.1按照使用特性分类 2.2按照传输速率分配 2.3按照信息交换的单位分类 三. IO设备的构成 3.1 IO的机械部件 3.2 IO的电子部件 3.2.1设备控制器&#xff08;IO控制器功能简介&#xff09; 3.2.2设备控制器&#xff08;IO控制器&…

【C++】红黑树的模拟实现

文章目录 一、红黑树的概念二、红黑树的性质三、红黑树节点的定义四、红黑树结构五、红黑树的插入操作六、红黑树的调整1.叔叔存在且为红2.叔叔不存在或者存在且为黑3.插入完整代码4.总结 七、红黑树的验证八、红黑树的删除九、红黑树与AVL树的比较十、红黑树的应用十一、红黑树…

d2l_第四章学习_Softmax Regression

x.1 Classification 分类问题 x.1.1 Classification和Regression的区别 注意&#xff0c;广义上来讲&#xff0c;Classification/Softmax Regression 和 Linear Regression 都属于线性模型。但人们口语上更习惯用Classification表示Softmax Regression&#xff0c;而用Regres…

C++特殊类的设计与类型转换

特殊类的设计与类型转换 特殊类的设计请设计一个类&#xff0c;只能在堆上创建对象请设计一个类&#xff0c;只能在栈上创建对象请设计一个类&#xff0c;只能创建一个对象(单例模式) C的类型转换 特殊类的设计 请设计一个类&#xff0c;只能在堆上创建对象 通过new创建的类就…

Baumer工业相机堡盟工业相机如何使用BGAPISDK对两个万兆网相机进行硬件触发同步(C++)

Baumer工业相机堡盟工业相机如何使用BGAPISDK对两个万兆网相机进行硬件触发同步&#xff08;C&#xff09; Baumer工业相机Baumer工业相机BGAPISDK和触发同步的技术背景Baumer工业相机使用BGAPISDK进行双相机主从相机触发1.引用合适的类文件2.使用BGAPISDK设置主相机硬件触发从…

C++中内存泄漏,内存溢出区别

C/C中内存泄露和内存溢出的区别 注&#xff1a;泄露为没有释放内存&#xff0c;溢出为分配空间不够&#xff0c;数据溢出了 内存溢出&#xff08;out of memory&#xff09;是指程序在申请内存时&#xff0c;没有足够的内存空间供其使用。 内存泄漏&#xff08;memory leak&…

【ROS_Driver驱动真实UR机械臂】

【ROS_Driver驱动真实UR机械臂】 1. 前言2. 安装fmauch_universal_robot和驱动3. 仿真3.1 启动gazebo3.2 启动move it规划3.3 启动rviz 4. 运行机械臂4.1 启动rviz4.2 启动示教器程序4.3 启动moveit4.4 启动rviz 5. 一些说明补充5.1 ur_calibration 提取标定信息5.2 自带程序5.…

从原理到实践:使用Mediacodec编码H265并实现解码H265码流

H265 H265&#xff0c;也称为HEVC&#xff08;High Efficiency Video Coding&#xff09;&#xff0c;是一种高效视频编码格式。它是H264&#xff08;AVC&#xff09;的后继者&#xff0c;也是ITU-T和ISO/IEC联合开发的标准。相比H264&#xff0c;H265可以在同样的视频质量下&…

【数据库原理与应用 - 第三章】数据库设计

数据库设计的步骤 需求分析阶段概念模型设计阶段 —— E-R图逻辑模型设计阶段 —— 关系模型物理结构设计阶段 数据库实施阶段数据库运行和维护阶段 目录 数据库设计的步骤 一、需求分析 1、主要任务 2、对象模型 二、数据库概念结构设计 1、概念数据模型 E-R图 1、概念…

Mybatis《学习笔记(22版尚硅谷)》

Mybatis简介 MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下&#xff0c;iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到GithubiBatis一词来源于“intern…