iPad适配指南

摘要:
“iPad适应指南”系列将介绍iPad上的一些特殊功能,如何更好地适应iPad,以及适应iPad时的一些注意事项。本文作为基础部分,主要介绍iPad的屏幕旋转和拆分、模式和拆分VC功能。从iOS 12开始,应用程序分屏的概念和操作更接近于当前的iPad OS。在iPad OS中,分屏应用程序有八种主要状态:水平屏幕1/3、水平屏幕1/2、水平屏幕2/3、水平屏幕满屏、垂直屏幕1/3、垂直屏幕2/3、垂直屏幕满屏和浮动窗口。当iPad退出后台或锁定屏幕时,该应用程序将被多次调用,因为它需要生成水平和垂直屏幕截图,以便在AppSwitch中显示。

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

➤微信公众号:山青咏芝(shanqingyongzhi)
➤博客园地址:山青咏芝(https://www.cnblogs.com/strengthen/ )
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址: https://www.cnblogs.com/strengthen/p/15401882.html
➤如果链接不是山青咏芝的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

热烈欢迎,请直接点击!!!

进入博主App Store主页,下载使用各个作品!!!

注:博主将坚持每月上线一个新app!!!

「iPad 适配指南」 这个系列会介绍在 iPad 上的一些特殊能力,如何更好地适配 iPad,以及适配 iPad 时的一些注意点。

本文作为基础篇,主要介绍 iPad 的转屏分屏、模态,和 SplitVC 能力。

如何判断 iPad 设备

如何判断设备, iPad 的各种形态

if UIDevice.current.userInterfaceIdiom == .pad {}
复制代码

在 M1 Mac 上运行的 iOS 应用取到的 userInterfaceIdiom属性为 .pad

在 Mac Catalyst 上运行的应用取到的 userInterfaceIdiom 属性为 .mac

分屏适配篇

iPad 和 iPhone 最大的不同是,我们往往在 iPhone 上会限定 App 的方向恒定为 Portrait,但在 iPad 上,我们不仅要处理旋转屏,还要处理各种分屏的情况。

分屏

iOS 上的分屏最早可以追溯到随 iOS 10 推出的 SlideOverSplit View画中画功能。从 iOS 12 开始,应用分屏的概念和操作比较接近于现在的 iPadOS。

在 iPadOS 中,分屏下的应用主要有 8 种状态:横屏 1/3 屏横屏 1/2 屏横屏 2/3 屏横屏全屏竖屏 1/3 屏竖屏 2/3 屏竖屏全屏,以及悬浮窗

iPad适配指南第1张

分屏可以通过多种操作唤起,最常见的是长按 Dock 中的图标,然后拖动到屏幕的一侧。

尺寸变化

UITraitCollection 是什么

View / VC 如何兼容大小的变化

viewWillTransition willTransition

无论是旋转屏幕,还是分屏,我们都可以收敛到「尺寸变化」这个概念上一起处理。

在此之前,需要先介绍 UITraitCollection 的概念。

UITraitCollection 是什么

traitCollectionUIViewUIViewControllerUIWindowUIWindowSceneUIScreen等的属性。

  • Transition 是指 vc 将会变化,变化的新属性集合会在 traitCollection 这个属性集合中。
  • traitCollection‌ 属性集合常用的属性有:纵横宽度的 sizeClass,是否是 darkMode 等属性

除了UIWindowScene是直接实现的属性,其他列举到的都是通过 UITraitEnvironment 协议来实现的:

public protocol UITraitEnvironment : NSObjectProtocol {

  @available(iOS 8.0, *)

  var traitCollection: UITraitCollection { get }

  /** To be overridden as needed to provide custom behavior when the environment's traits change. */

  @available(iOS 8.0, *)

  func traitCollectionDidChange( _ previousTraitCollection: UITraitCollection?)

}
复制代码

traitCollectionDidChange一般会用在响应 iOS 界面环境的变化,对窗口大小变化的兼容会在接下来的一节中讲到。

View / VC 兼容大小的变化

约束布局不必考虑尺寸的变化

  • 对于 View,可以在layoutSubviews中进行 frame 布局或响应尺寸的变化。当窗口大小发生变化的时候,VC 会调用 View 的该方法。
  • 对于 VC,有两种策略:
  1. viewWillLayoutSubviews中进行布局
  2. 可以在以下两个方法中进行布局的调整:
// UIViewController 实现了这个协议

public protocol UIContentContainer : NSObjectProtocol {

  @available(iOS 8.0, *)

  func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)

  @available(iOS 8.0, *)

  func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator)

}
复制代码
  • 调用时机的区别在于:

    • VC 出现和大小变化时都会调用viewWillLayoutSubviewswillTransition
    • VC 出现时,如果不主动改变 view 大小,不会调用viewWillTransition,仅当 view 的大小变化时才会调用
  • 2 中的两个函数的区别在于,窗口大小变化时:

    • willTransition会先被调用。可以通过重写该方法获得即将变成的新 traitCollection

      • 注意:此时取 view/vc/window 的 traitCollection 仍为旧值
    • viewWillTransition后被调用。可以通过重写该方法获得即将变成的新 size

      • 注意:此时取 view/vc/window 的 traitCollection 和 bounds.size 仍为旧值
      • 最佳实践:如果需要在 viewWillTransition 中获取即将变成的新 traitCollection,可以考虑在 vc 持有一个 lastTraitCollection,并且在 willTransition 时更新其值。

这三种方法不仅仅会在上述情形中被调用。

App 在 iPad 退出后台或锁屏时,因为要生成横屏和竖屏的截图以便在 App Switcher 中显示,都会被多次调用。

详见后文「锁屏/退到后台时在 iPad 上的特殊情况」

UIScreen 的使用

大家可能早已习惯直接使用 UIScreen.main.bounds。这在过去的一台设备只有唯一屏幕、一个屏幕只有唯一应用情况下是没有问题的。但事情正在发生改变:在 iPadOS 上,一个屏幕已经能显示多个应用了,在 Apple Silicon Mac 上,一个设备也能有多个显示内容不一样的屏幕,应用并不一定会在 UIScreen.main 上显示。

我们应该遵循的原则是:在每个 UIView 中,获取自身的 bounds 属性,或者利用元素间的相对关系 Auto Layout 进行布局。应该尽量避免获取设备本身的宽高来进行布局。

SizeClass 介绍

介绍 sizeClass 概念,以及各种 iOS 窗口尺寸对应的 CR 值

概念

日常我们所说的Size Class,是UITraitCollection中的两个属性:

 @available(iOS 8.0, *)

open class UITraitCollection : NSObject, NSCopying, NSSecureCoding {

  /// 水平 size class,最常用

  open var horizontalSizeClass: UIUserInterfaceSizeClass { get }

  /// 竖直 size class,用的少

  open var verticalSizeClass: UIUserInterfaceSizeClass { get }

}
复制代码

Size Class 将界面宽度分成了 CompactRegular 两种类型。

 @available(iOS 8.0, *)

public enum UIUserInterfaceSizeClass : Int {

  /// 未指定

  case unspecified = 0

  /// 紧致

  case compact = 1

  /// 正常(宽松)

  case regular = 2

}
复制代码

对于每个 View / VC / Window / WindowScene / Screen,都有 size class 的概念。

Size Class 对我们最重要的意义是:

响应式布局最重要的即是断点。所谓断点,就是一个分界线,在这个分界线的两边,我们会采取不同的布局策略。而 Size Class 给我们提供了关于断点的指导。

系统水平方向 Size Class 规则

  • 目前在 iPhone 竖屏时,horizontalSizeClass都是Compact,其他情况比较复杂,参考官方文档,不展开赘述;
  • 在 iPad 上,全屏横屏2/3分屏都是Regular
  • 横屏1/2分屏时,只有 12.9 寸的 iPad 是Regular
  • 除此之外的其他情况都是Compact

iPad适配指南第2张

详见官方文档:Size Classes - HIG

布局控件篇

模态控件

介绍 modalPresentationStyle 各种样式的效果 以及着重介绍一下 popover 的概念

iPad适配指南第3张iPad适配指南第4张

在 iPad 上,我们经常看到这样的页面。看起来两者差异很大,似乎需要做很多的适配,但其实代码很简单,我们只需要两行代码,就能同时完成在 iPhone 上和 iPad 上的适配:

vc.modalPresentationStyle = .formSheet

self.present(vc, animated: true)
复制代码

这里涉及到了 modalPresentationStyle 的概念。

我们知道,一个 VC 可以被 push,也可以被 present。

两者在用法上的区别是,present 的页面会阻挡用户的其他操作,使其专注在当前页面上。

Sheet

在 iPad 上有两种种最常见的样式:.formSheet.pageSheet,这三种都是 present 前可以设置给 VC 的样式。

在 iPhone 上,两种 Sheet 的样式没有什么分别:

iPad适配指南第5张

在 iPad 上 formSheet 和 pageSheet 的区别是:

  • pageSheet 的浮窗大小是系统根据系统字体大小确定的,不能修改大小
  • formSheet 和接下来要提到的 popover 的大小,都可以通过 vc 的 preferredContentSize 来指定实际大小。
iPad适配指南第6张iPad适配指南第7张
pageSheet适合信息密度较高、阅读写作formSheet默认大小,适合信息密度较低或自定义大小的场景

iOS 13 对 formSheet 的窄屏样式从 fullScreen 变成了现在的层叠卡片样式

对于 formSheetpageSheet,在 iPad 上有手势下滑返回的自带功能。

如果希望介入手势下滑事件,可在 UIAdaptivePresentationControllerDelegate 中进行处理。

Popover 气泡

iPad适配指南第8张

Popover 是 iPad 上非常常见的一种交互元素。

前面我们介绍到的 modalPresentationStyle,还有一种取值即为 .popover

但与前面几种我们提到的 Style 不同的是,除了简单的指定 modalPresentationStyle 之外,我们还需要设置几个属性:

// 指定样式

pushvc.modalPresentationStyle = .popover

// 指定 Popover 指向的矩形

pushvc.popoverPresentationController?.sourceRect = btn.frame

// 指定 Popover 指向的 View,必须指定,否则会崩溃

pushvc.popoverPresentationController?.sourceView = self.view

// 指定 Popover 允许的箭头朝向

pushvc.popoverPresentationController?.permittedArrowDirections = .up

self.present(pushvc, animated: true)
复制代码

modalPresentationStyle

我们以 iPad Pro 11-inch, iOS 14, SplitVC detailVC(yellow) present(purple 40% 透明度)VC 的 case 为例,简单介绍一下所有 modalPresentationStyle 的取值区别:

横屏全屏iPad适配指南第9张iPad适配指南第10张iPad适配指南第11张iPad适配指南第12张iPad适配指南第13张iPad适配指南第14张
竖屏全屏iPad适配指南第15张iPad适配指南第16张iPad适配指南第17张iPad适配指南第18张iPad适配指南第19张iPad适配指南第20张
窄屏&iPhoneiPad适配指南第21张iPad适配指南第22张iPad适配指南第23张iPad适配指南第24张iPad适配指南第25张iPad适配指南第26张
类型fullScreenpageSheetformSheetcurrentContextoverFullScreenoverCurrentContext
大小特点覆盖全屏更大尺寸的模态可自定义大小的模态,默认大小如图只覆盖当前区域覆盖全屏只覆盖当前区域

当然,系统也提供了 custom 样式,以提供自定义动画和样式的能力。

over** 的区别是:

over* 不会将覆盖的视图从视图层级撤下

iOS 15 | Customize and resize sheets in UIKit

Video: Customize and resize sheets in UIKit - WWDC 2021 - Videos - Apple Developer

在 iOS 15 中,Sheets 又有了一些新能力:

我们可以更精细化地控制 Sheets 的垂直高度了,比如创建一个半屏 Sheet,或者让 Sheet 可以在半屏高度停靠(Dedents):

iPad适配指南第27张

我们可以移除 Sheets 下的阴影遮罩,让我们可以在展示 Sheet 的时候与下层 View 交互;

或者在 Compact 屏幕下展示非全屏 Sheet

iPad适配指南第28张iPad适配指南第29张

所有的新特性都可以通过新 API:UISheetPresentationController 来进行行为的控制。

当 VC 的 modalPresentationStyle 为 formSheet / pageSheet (by default) 时,我们可以这样取得 UISheetPresentationController

// Get a sheet

if let sheet = viewController.sheetPresentationController {

  // Customize the sheet

}

present(viewController, animated: true)
复制代码

路由跳转

UISplitViewController

介绍 UISplitViewController 是什么

master detail 概念

showMaster / showDetail 的概念

各种 displayMode 代表什么

为了更好地利用 iPad 更大屏幕的尺寸,系统提供了 UISplitViewController,以在宽屏情况下并列显示多个视图

iPad适配指南第30张

上图是 iOS 14 中 UISplitViewController 更新的新接口,允许三栏同时展示。我们可以在系统自带的 邮件app 看到实际的效果。

iOS 14 更新了新的初始化接口:init(style:)。通过这个接口我们可以在初始化时设置两栏或者三栏的布局:

iPad适配指南第31张

DisplayMode

规定术语:

Master / Primary:两栏时,展示在左侧的单栏

Detail / Secondary:两栏时,展示在右侧的详细页面

UISplitViewController 有多种显示模式,我们称之为 DisplayMode。这里简要介绍一下:

 iPad适配指南第32张iPad适配指南第33张iPad适配指南第34张iPad适配指南第35张iPad适配指南第36张iPad适配指南第37张
automaticsecondaryOnlyprimaryHiddenoneBesideSecondaryallVisableoneOverSecondaryprimaryOverlaytwoBesideSecondary iOS 14 availabletwoOverSecondary iOS 14 availabletwoDisplaceSecondary iOS 14 available
自动模式,根据屏幕大小自动切换只展示 detail 页Master 和 detail 并列展示Master 盖住了 detail两栏与 detail 并列两栏盖住了 detail两栏将 detail 向右挤开,参考 邮件.app

简单概括:Bseide 意为并列显示,over 意为上层会覆盖下层的一个部分,Displace 意为上层会挤开下层。

常用接口

路由行为

如果是使用 init(style:) 初始化的 iOS 14 列风格 的 SplitVC,一切会变得省心很多:

  • setViewController(_:for:) 来设置 VC 应该展示在哪一列

  • viewController(for:) 来获取指定列的 VC

  • SplitVC 会自动把所有的 childVC 用 navigationController 包住。

    • 如果设置的时候没有提供 navigationController,SplitVC 会自动创建一个。
    • 通过 SplitVC 的 children 属性可以找到 navigationController。
  • show(_:) 或者 hide(_:) 来展示或隐藏指定列

如果是传统风格的 SplitVC(只支持 master & detail 的显示,不支持更多栏):

  • 如果需要,应该手动为 master 和 detail 手动设置 navigationController 以实现路由跳转。
  • 直接设置 viewControllers 属性,默认第一个为 master,第二个为 detail,会忽略更多(如果有)
  • 使用 show(_:sender:) 来在 master 中找到 navigationController 进行 push vc
  • 使用 showDetailViewController(_:sender:) 来 在 detail 中找到 navigationController 进行 push vc
尺寸变化

在 iPad 上,用户可能进行的分屏操作会突然改变程序的视图大小。当视图较窄时,SplitVC 的分栏布局可能不再适合,我们可能需要将所有栏中的 viewControllers 进行合并。当视图变宽时,我们又需要将 viewControllers 分配到不同的列当中。在这里我们称之为 Collapse & Expand。

我们可以在 SplitVC 的 delegate 中控制上述行为:

public protocol UISplitViewControllerDelegate {

  // Return the view controller which is to become the primary view controller after `splitViewController` is collapsed due to a transition to

  // the horizontally-compact size class. If you return `nil`, then the argument will perform its default behavior (i.e. to use its current primary view

  // controller).

  @available(iOS 8.0, *)

  optional func primaryViewController(forCollapsing splitViewController: UISplitViewController) -> UIViewController?



   

  // Return the view controller which is to become the primary view controller after the `splitViewController` is expanded due to a transition

  // to the horizontally-regular size class. If you return `nil`, then the argument will perform its default behavior (i.e. to use its current

  // primary view controller.)

  @available(iOS 8.0, *)

  optional func primaryViewController(forExpanding splitViewController: UISplitViewController) -> UIViewController?



   

  // This method is called when a split view controller is collapsing its children for a transition to a compact-width size class. Override this

  // method to perform custom adjustments to the view controller hierarchy of the target controller. When you return from this method, you're

  // expected to have modified the `primaryViewController` so as to be suitable for display in a compact-width split view controller, potentially

  // using `secondaryViewController` to do so. Return YES to prevent UIKit from applying its default behavior; return NO to request that UIKit

  // perform its default collapsing behavior.

  @available(iOS 8.0, *)

  optional func splitViewController( _ splitViewController: UISplitViewController, collapseSecondary secondaryViewController: UIViewController, onto primaryViewController: UIViewController) -> Bool



   

  // This method is called when a split view controller is separating its child into two children for a transition from a compact-width size

  // class to a regular-width size class. Override this method to perform custom separation behavior. The controller returned from this method

  // will be set as the secondary view controller of the split view controller. When you return from this method, `primaryViewController` should

  // have been configured for display in a regular-width split view controller. If you return `nil`, then `UISplitViewController` will perform

  // its default behavior.

  @available(iOS 8.0, *)

  optional func splitViewController( _ splitViewController: UISplitViewController, separateSecondaryFrom primaryViewController: UIViewController) -> UIViewController?

}
复制代码

锁屏/退到后台时在 iPad 上的特殊情况

在 iOS 上,因为需要在 App Switcher 中显示各应用在横屏、竖屏、分屏情况下的界面预览,所以系统会提前在应用锁屏或退到后台时,对应用进行模拟界面变化并截图。

系统函数名为beginSnapshotSession。

在 iPad 上的整个模拟界面变化的过程中,一般会模拟横屏、竖屏、分屏等几种大小。处于最上层的 VC 可能会收到多次 willTransition / viewWillTransition / viewWillLayout 的调用。

在存在 SplitVC 的情况中,甚至因为模拟分屏,导致 mergeMasterAndDetail 时隐藏了 VC,调用到 VC 的 viewDidDisappear,也是有可能的。


作者:飞书技术
链接:https://juejin.cn/post/6986538271698321439
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

免责声明:文章转载自《iPad适配指南》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Win7 安装7zip后无7zip右键菜单的解决办法【转载】阮一峰网络日志中的JWT入门下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Sencha Touch开发实例:有动画效果iPad的网站首页

在51CTO的专题“Sencha Touch入门教程”中我们已对如何使用Sencha touch进行开发有了初步的认识。在本系列教程中,将学习如何使用Sencha Touch,开发一个适合在iPad上运行的网页应用,并详细讲解其中的技巧。本文的阅读对象为对Sencha Touch有一定基础的读者,如果读者不熟悉相关内容,请先查看51CTO的专题。 构图设计...

itouch/iphone/ipad充不上电的解决办法

  itouch/iphone/ipad长时间没有用,或是没电自己关机了,再充电开机变白苹果了,网上的方法用遍了都不管用,今天找到一个,很有效,特此分享给大家,具体操作如下:   首先把itouch/iphone/ipad连接至电脑,在出现白苹果的时候,按住电源键和Home键(主菜单键),5秒左右,就会黑屏。然后立刻按住电源键3秒,接下来不松开电源键的同时...

初识iOS9 iPad新特性SlideView和SplitView的适配

苹果刚发布了iOS9,在iPad上新增了两个新的特性SlideView和SplitView,前者可以在不关闭当前激活APP的情况下调出来另外个APP以30%比例显示进行操作使用,后者允许同时运行两个APP以50%50%,70%30%比例运行,感觉非常方便。 然而,方便了用户的同时却恶心了开发者,在同一屏幕运行两种APP的时候势必APP显示比例发生改变,那么...

基于HTML5的iPad电子杂志横竖屏自适应方案

基于HTML5的iPad电子杂志横竖屏自适应方案 (转载自:http://www.yeeach.com/?p=1172) 基于HTML5来制作iPad电子杂志,横屏及竖屏自适应是个大问题,查找了半天资料,没有一篇像样的文章可供参考。将思路及例子分享一下。例子并不严谨和规范,仅供参考。 大致思路: 1、对横屏(portrait)和竖屏(landscape)情...

iPad新手必看-各种文件存放路径详解[转]

iPad运行的iOS系统和我们平时使用的Windows有这巨大的不同,包括文件存储方式上,iOS有着严格的目录规划,对于想更大程度扩展iPad系能的朋友来所,知晓iPad系统常用文件存放路径是必不可少的,下面是小编收集到的ipad各种文件存放路径详解,希望对iPad新手有所帮助。   1、【/Applications】   常用软件的安装目录   2. 【...

用HTML,CSS和JavaScript创建iPhone/iPad应用程序

象大多数iPad粉丝和程序员一样,我有一个梦想,那就是如果不需要昂贵的Apple设备,不要繁琐的审批程序,不要为发布应用交钱就能开发iPad 应用程序该多好.现在这个梦想就要实现了,那就是网页应用程序. 那么,怎样才能让网页象看起来象本机程序一样呢?你需要做这些工作: 全屏幕:(去掉浏览器的地址栏和按钮栏);防止窗口滚动和缩放; 响应多点触摸和手势事件;...