热门标签 | HotTags
当前位置:  开发笔记 > IOS > 正文

iOS横竖屏旋转内容总结

本篇文章给大家总结了iOS横竖屏旋转的相关内容以及实现的代码分享,有需要的朋友参考下吧。

一、前言

Swift版本 4.0

Xcode版本 9.2

以前接触到的项目需求中,几乎都是全竖屏展现界面,所以我也来得省事,直接在TARGETS中的界面方向选项中只勾选竖屏,这样就满足了需求。

但最近的项目中,产品突然增加了一个需求,需要部分界面支持旋转,这才来研究了一下屏幕旋转的问题!

需要紧急解决问题的道友直接看3.3

二、屏幕旋转相关知识

2.1 三个方向的理解和联系

UIDeviceOrientation: 设备方向

public enum UIDeviceOrientation : Int {
 case unknown
 case portrait // 设备vertically方向, home键在下方
 case portraitUpsideDown // 设备vertically方向, home键在上方
 case landscapeLeft // 设备horizontally方向, home键在右方
 case landscapeRight // 设备horizontally方向, home键在左方
 case faceUp // 设备flat方向, 屏幕朝上
 case faceDown // 设备flat方向, 屏幕朝下
}

 

从设备方向的命名就能看出来这个枚举的含义,这里指的是物理设备(即iPhone)的方向。

UIInterfaceOrientation: 界面方向

public enum UIInterfaceOrientation : Int {
 case unknown
 case portrait
 case portraitUpsideDown
 case landscapeLeft
 case landscapeRight
}

而界面方向指屏幕中显示内容的方向,它的方向和Home键的方向是一致的。仔细观察一下屏幕旋转就能理解UIDeviceOrientation和UIInterfaceOrientation了,我们把手机转向左边,可以看到界面随之才转向右边。

UIInterfaceOrientationMask: 是用来控制允许转向的方向,对应UIInterfaceOrientation

public struct UIInterfaceOrientationMask : OptionSet {
 public init(rawValue: UInt)
 public static var portrait: UIInterfaceOrientationMask { get }
 public static var landscapeLeft: UIInterfaceOrientationMask { get }
 public static var landscapeRight: UIInterfaceOrientationMask { get }
 public static var portraitUpsideDown: UIInterfaceOrientationMask { get }
 public static var landscape: UIInterfaceOrientationMask { get }
 public static var all: UIInterfaceOrientationMask { get }
 public static var allButUpsideDown: UIInterfaceOrientationMask { get }
}

2.2 观察屏幕旋转并作出响应

2.2.1 观察设备方向并响应

// 没有生成通知
if !UIDevice.current.isGeneratingDeviceOrientationNotifications {
 // 生成通知
  UIDevice.current.beginGeneratingDeviceOrientationNotifications()
}
// 锁定竖屏,依然有效,例如faceUp.
NotificationCenter.default.addObserver(self, 
          selector: #selector(handleDeviceOrientationChange(notification:)),       name:NSNotification.Name.UIDeviceOrientationDidChange,
          object: nil)
@objc private func handleDeviceOrientationChange(notification: Notification) {
 // 获取设备方向
 let orientation = UIDevice.current.orientation
 switch orientation {
  case .landscapeRight:
   // iOS8之后,横屏UIScreen.main.bounds.width等于竖屏时的UIScreen.main.bounds.height
   print(UIScreen.main.bounds.width)
   print("landscapeRight")
  default: break
 }
}

注销

deinit {
 NotificationCenter.default.removeObserver(self)
 UIDevice.current.endGeneratingDeviceOrientationNotifications()
}

2.2.2 观察界面方向并响应

和上面类似不过观察的name为

// 锁定竖屏,无效,通知方法不会触发
NSNotification.Name.UIApplicationWillChangeStatusBarOrientation
NSNotification.Name.UIApplicationDidChangeStatusBarOrientation

获取界面方向

let statusBarOrientation = UIApplication.shared.statusBarOrientation

2.2.3 建议

这里建议监听界面方向,原因有二:

监听设备方向,会返回多个方向,例如portrait和faceUp不冲突。

监听设备方向,上面提到,先是设备旋转,随之界面旋转,这里就有一个问题,我们操作界面时,可能界面还没有旋转。

三、问题解决实战

需要实现部分界面可旋转,部分界面锁定竖屏,首先我们需要配置TARGETS中的Device Orientation,这里是总开关,默认勾选了如图方向:

如果你确定整个项目只有竖屏,直接只勾选Protrait完事,不过像我现在这样,可能突然一个需求改变就不得不继续适配,哈哈。

这里的配置不要和代码控制的方向相冲突,不然会引发奔溃。

3.1 控制屏幕旋转的函数

// 默认为true
override var shouldAutorotate: Bool {
 return true
}
// 支持的旋转方向
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
 return .landscapeLeft
}
// 模态切换的默认方向
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
 return .landscapeRight
}

这三个属性都重写的UIViewController的属性。哎,看到模态切换,这里再给自己挖坑一个,以前研究了一会模态切换,只不过没写成总结,后面会写出来(:。

并且这三个方法会受到控制器层级的影响,也就是如果当前控制器配置支持旋转,如果他的导航控制器,乃至Tabbar控制器不支持旋转,当前控制器的配置也不会生效。

3.2 不同根控制器情况下的解决

核心问题: 需要旋转的界面是少数,大多界面需要锁定竖屏。

3.2.1 根控制器为UIViewController

对应Demo配置:

这种情况的APP可以说是非常少了,不过还是对后面的情况有所帮助。

设置BaseVC,在其中的配置锁定竖屏:

class BaseVC: UIViewController {
 override var shouldAutorotate: Bool {
  return false
 }  
 override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
  return .portrait
 }
 override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
  return .portrait
 }  
 override func viewDidLoad() {
  super.viewDidLoad()
 } 
}

然后其余控制器继承BaseVC,需要旋转的控制器单独再次重写方法。

3.2.2 根控制器为UINavigationController

对应Demo配置:

我们可以获取到当前显示层级的控制器,并拿出它的属性赋给UINavigationController

class BaseNavC: UINavigationController {
 override var shouldAutorotate: Bool {
  return self.viewControllers.last?.shouldAutorotate ?? false
 }
 override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
  return self.viewControllers.last?.supportedInterfaceOrientations ?? .portrait
 }
 override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
  return self.viewControllers.last?.preferredInterfaceOrientationForPresentation ?? .portrait
 }
 override func viewDidLoad() {
  super.viewDidLoad()
 }
}

3.2.3 根控制器为UITabBarController

对应Demo配置:

class BaseTabBarC: UITabBarController {
 override var shouldAutorotate: Bool {
  return self.selectedViewController?.shouldAutorotate ?? false
 }
  
 override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
  return self.selectedViewController?.supportedInterfaceOrientations ?? .portrait
 }
  
 override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
  return self.selectedViewController?.preferredInterfaceOrientationForPresentation ?? .portrait
 }
  
 override func viewDidLoad() {
  super.viewDidLoad()
 }
}

同理,我们只需要获取当前选中的控制器的配置赋给UITabBarController,这样一层一层就配置好了!

3.3 最简单的实现方式

对应Demo配置:

在查询屏幕旋转相关资料的时候我发现屏幕旋转时会最后调用Appdelegate中的:

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) 
-> UIInterfaceOrientationMask {
}

然后我立马想到一个超级简单的方法,那就是定义一个全局变量或者缓存一个bool值来进行判断,如下:

func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) 
-> UIInterfaceOrientationMask {
 if isAllowAutorotate {
  return [.portrait, .landscapeLeft, .landscapeRight]
 }
 else {
  return .portrait
 }
}

然后默认isAllowAutorotate这个全局变量为false,在需要旋转的控制器中:

override func viewWillAppear(_ animated: Bool) {
  super.viewWillAppear(animated)
  isAllowAutorotate = false
 }
  
 override func viewWillDisappear(_ animated: Bool) {
  super.viewWillDisappear(animated)
  isAllowAutorotate = true
 }
}

这样就不用麻烦的去搞那些继承什么的了!

四、后记和Demo

https://github.com/swordjoy/ScreenRotationDemo

以上就是本次小编整理的全部内容,感谢大家的支持,如果还有任何相关问题可以在下方的留言区讨论。


推荐阅读
  • 使用Swift 2.2创建我的第一个Xcode应用
    本文将指导您如何使用Xcode 6搭建并运行一个简单的iOS应用程序。从启动Xcode到执行首个应用,每个步骤都将详细介绍。 ... [详细]
  • 本教程旨在指导开发者如何在Mac上设置React Native的开发环境,以进行iOS应用的开发。文中详细介绍了必要的软件安装步骤,包括Xcode、Homebrew、Node.js、Watchman以及React Native CLI等工具的安装方法。 ... [详细]
  • iOS中UITabBar与UINavigationController的集成及样式自定义
    探讨如何在iOS开发中有效结合UITabBarController和UINavigationController,并实现自定义样式。项目源码已托管至GitHub:https://github.com/zcsoft/ZCTabNav ... [详细]
  • 本文档提供了在iOS开发中使用OClint进行静态代码分析的详细步骤,包括安装、更新和使用方法,以及相关资源链接。 ... [详细]
  • 本文介绍了iOS应用开发的主要框架,包括Foundation、UIKit、CoreData及CoreGraphics等,并探讨了开发iOS应用所需的硬件和软件环境,以及推荐的编程语言。 ... [详细]
  • 本文总结了在使用React Native开发过程中遇到的一些常见问题及其解决方法,包括配置错误、依赖问题和特定组件的使用技巧。 ... [详细]
  • 本文探讨了在UC浏览器中调用分享面板后,图片无法正常显示的问题,并提供了详细的解决方法和代码示例。 ... [详细]
  • FinOps 与 Serverless 的结合:破解云成本难题
    本文探讨了如何通过 FinOps 实践优化 Serverless 应用的成本管理,提出了首个 Serverless 函数总成本估计模型,并分享了多种有效的成本优化策略。 ... [详细]
  • 本文介绍了如何在Xcode中通过自定义文件模板来添加个性化的注释,以提高代码的可读性和维护性。具体步骤包括打开Xcode的应用包,定位到文件模板目录,并对相关文件进行编辑。 ... [详细]
  • 本文将带您了解Cocos家族的不同版本和分支,特别是Cocos Creator的发展历程及其核心特性,帮助初学者快速入门。 ... [详细]
  • 如何使用Ionic3框架创建首个混合开发应用
    混合开发是指结合原生(Native)与网页(Web)技术进行移动应用开发的方法。本文将详细介绍如何利用Ionic3这一流行的混合开发框架,从环境搭建到创建并运行首个应用的全过程。 ... [详细]
  • 本文详细介绍如何在 macOS 上编译 FFmpeg 3.1.1,并将其集成到 iOS 项目中,包括必要的环境配置和代码示例。 ... [详细]
  • 作为一名在大型手机游戏公司工作的程序员,尽管主要负责游戏逻辑和内容的开发,但对iOS底层开发接触较少。现在有了iPhone和可以虚拟MAC环境的电脑,希望能找到有效的iOS开发学习路径。 ... [详细]
  • Xcode 快捷键与实用技巧
    在iOS开发过程中,熟练掌握Xcode的快捷键可以显著提升工作效率,减少不必要的鼠标操作,让开发者更加专注于代码编写。本文将介绍一些常用的Xcode快捷键及技巧,帮助开发者提高开发效率。 ... [详细]
  • Android开发经验分享:优化用户体验的关键因素
    随着Android市场的不断扩展,用户对于移动应用的期望也在不断提高。本文探讨了在Android开发中如何优化用户体验,以及为何用户体验的重要性超过了技术本身。 ... [详细]
author-avatar
mobiledu2502857983
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有