iPad 多任務 Spilt View & Size Class
iPad 多任務 Spilt View & Size Class
一、多任務簡介
iOS 9 以后iPad新增了多任務的支持,主要形式有三種:
- Slide Over (側邊快捷打開)
- Spilt View (多任務分屏)
- Picture in Picture (畫中畫)
1. Picture in Picture
使用系統AVKit或者AVFoundation庫提供的新的API替換掉老的 MPMoviePlayerViewController, MPMoviePlayerViewController,做一些調整即可。
2. Slide Over
Slide Over支持起來比較簡單,程序基本上不需要做任何代碼級別的適配。
Tips: App交互設計的時候,應該避免采用屏幕右側向左滑這一類的交互。因為iOS9以后系統攔截了該事件用來觸發Slide Over & Spilt View。
例如網易新聞iPad端的評論劃出功能
3. Spilt View
多任務分屏功能,我們今天主要探討的內容,后續章節有詳細描述。
## 二、是否要支持多任務分屏?
1. 是否需要適配
引用官方的說法
Adopt Slide Over and Split View unless you have a specific reason not to. From a customer’s perspective, an iOS 9 app that doesn’t adopt Slide Over and Split View feels out of place.
簡單翻譯一下:“最好添加Slide Over & Spilt View的適配,除非你有足夠的原因。不做新特性的適配會讓用戶覺得你的程序out了”
當然也有例外的情況,如果你的程序屬于以下兩種,那么不需要去做多任務的適配:
- 以拍照為主要功能的程序
- 需要全屏交互的程序,例如需要使用到傳感器的游戲類應用
2. 如何禁用多任務分屏
要禁掉App的多任務分屏支持,只需要在App的info.plist中添加一個“UIRequiresFullScreen”的key,設置value為“YES”即可。
到這兒了,如果你的不需要支持多任務分屏的話那么就后面的內容就可以跳過了。
## 三. 適配前需要明確的幾個點
1. 轉變觀念
首先有一個觀念我們得轉變過來,同一時刻運行在前臺的App不再限制為一個。如果用戶進入了多任務分屏模式的話,那么就會有兩個App同時運行在前臺。
Both Apps in Spilt View are running in the foreground.
雖然兩個App同時運行在前臺時,地位卻不一樣,Primary(老大,通常在左邊)和Secondary(老二,通常指的是右邊的應用)。
只有老大:
- 擁有狀態欄的控制權限
- 可以處理外接屏幕的顯示(通常使用UIScreen實現)
- 可以顯示畫中畫窗口
- 可以占據2/3寬的屏幕,而老二最多只能占據屏幕寬度的一半
避免使用UIScreen的bounds來處理App應該的展示區域,最好使用UIWindow的bounds來代替。
因為在多任務分屏下UIScreen還是那個Screen,只不過App的keyWindow不再時時刻刻充滿UIScreen了,顯示在什么位置,顯示多大面積,全部取決于用戶使用設備的姿勢。
2. 面臨的問題
前面提到過了,當進入多任務分屏模式以后,將會有兩個App同時運行在前臺。試想有兩個App同時需要使用CPU,GPU,內存,I/O及其它的硬件資源,要想保持良好的用戶體驗,就需要我們對自己的App做很多性能調優的方面的工作。
關于性能調優方面的知識,可以參考:Adopting Multitasking Enhancements on iPad
Every iOS app—even one that opts out of using multitasking features—needs to operate as a good citizen in iOS 9.
Now, even full-screen apps don’t have exclusive use of screen real estate, the CPU, memory, or other resources.
同時蘋果基于保證用戶體驗的角度,對于支持做了硬件層面的限制如下:

前面提到了,Primary App比Secondary App擁有諸多優勢,但是有一點是一樣的:
當系統收到內存警告時,無論是Primary App還是Secondary App均可能被Kill掉
## 四. 如何適配Spilt View
如果遵循iOS 8引入的新的UI最佳實踐,那么適配多任務適配將會是一件很容易的實情。可是問題關鍵問題就出在了這個“如果”上。
iOS 8推出以后蘋果提的最多的“Adaptivity”,以及新引入了Size Class體系,并提出了讓我們忘記設備方向的概念,所以的這一切都是在為了我們能夠方便的實現App的布局。
通常開發App的UI框架的陳舊加上交互設計只考慮橫豎屏(甚至只考慮一個方向)導致了我們適配Spilt View的難度比較大。
1. 適配Spilt View的幾點要求
-
Xcode 7 及之后編譯
-
使用iOS 9 及之后的SDK
-
使用"LaunchScreen.storyboard"代替launch.png之類的圖片,完成啟動畫面定制。Xcode7之后新建工程會自動幫忙創建該文件并設置Info.plist,對于已存在的老工程需要我們手動創建該文件,并在Info.plist中做相應配置。
PS. 蘋果要求LaunchScrenn.storyboard中必須使用Autolayout布局,還在用全手寫布局的朋友們,該考慮切換到Auto Layout布局了。
-
支持四個方向
2. 多任務分屏模式切換時發生了什么
在多任務分屏模式時,App展示的尺寸完全取決于用戶,可能會占屏幕的3/10, 5/10, 7/10等,再加上豎屏時候那兩個奇葩的比例。一個App要完全支持iPad的多任務分屏,如果使用硬編碼的方式,那么需要考慮5套布局,有沒有想死的感覺。
先別著急,接著往下看。
當多任務分屏模式下,用戶操作應用之間的分割區改變兩個應用的顯示比例時,系統會同時調用兩個App的“applicationWillResignActive”方法。然用戶完成操作的時候系統會再同時調用連個App的“applicationDidEnterBackground”方法。

關于如何相應App狀態變換,請看文檔:Strategies for Handling App State Transitions
與此同時,系統會通過以下兩個方面告知我們分屏狀態的改變:
- Window尺寸的變化
- RootVC 的Size Class的變化
這兩個點配合起來才能完成多任務分屏顯示模式切換的響應。
因為多任務分屏顯示模式的變化并不總是伴隨著Size Class的變化,例如從3/10 --> 5/10的變換的時候水平方向的Size Class一直都是Compac模式,但是尺寸(寬度)卻發生了變化。
3. 如何響應Window尺寸變化
當Window尺寸變化時通常伴隨著VC的viewDidLayoutSubViews活著View的layOutSubViews方法的調用,我們可以在這些方法里面重新計算位置,完成UI布局的刷新。
還有一個更好的方法就是Auto Layout,我們在程序一開始布局的時候通過指定元素之間的約束來描述布局,這樣在View的尺寸變化的時候系統會根據我們制定的約束條件自動完成布局的刷新。
4. 如何響應Size Class的變化
我是代碼黨,界面布局大部分都是靠代碼實現,Storyboard幾乎不用。在一開始我查找資料的時候一搜Size Class出來的都是教人怎么在Storyboard中完成界面布局的文章。還以為Size Class就是專門為Storyboard設計的。
后面在搜索終于被我發現了一些端倪,iOS 8中 引入了兩個Protocol:UITraitEnvironment 和 UIContentContainer,以及一個類UITraitCollection。
其中UITraitEnvironment協議的定義如下:
/*! Trait environments expose a trait collection that describes their environment. */
@protocol UITraitEnvironment <NSObject>
@property (nonatomic, readonly) UITraitCollection *traitCollection NS_AVAILABLE_IOS(8_0);
/*! To be overridden as needed to provide custom behavior when the environment's traits change. */
- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection NS_AVAILABLE_IOS(8_0);
@end
可以看到該協議定義了一個traitCollection的屬性,還有一個用來通知traitCollection改變的方法。系統中我們可以想到的UI類都實現了這個協議,包括: UIScreen, UIWindow, UIViewController, UIPresentationController, 以及UIView.
UIContentContainer協議則定義了幾個VC級別的用來響應TraitCollection變化的方法,UIViewController和UIPresentationController都實現了該協議。通過該協議定義的方法我們可以在Size Class變化的時候做一些動畫。
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator NS_AVAILABLE_IOS(8_0);
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator NS_AVAILABLE_IOS(8_0);
UITraitCollection類則定義了一些屬性用來描述設備特性,如下所示:
horizontalSizeClass
verticalSizeClass
displayScale
userInterfaceIdiom
forceTouchCapability
到這兒我們終于看見了SizeClass的身影了,而Size Class的定義如下:
typedef NS_ENUM(NSInteger, UIUserInterfaceSizeClass) {
UIUserInterfaceSizeClassUnspecified = 0,
UIUserInterfaceSizeClassCompact = 1,
UIUserInterfaceSizeClassRegular = 2,
} NS_ENUM_AVAILABLE_IOS(8_0);
iOS 系統把UI的顯示模式抽象為三種:Unspecified(對應StoryBoard中的any),Compact,Regular。
非多任務分屏下,常見設備的Size Class如下:

可以看出在非多任務分屏模式下,iPad 無論在橫屏和豎屏下寬,高都是Regular。
進入多任務分屏模式以后Size Class如下:

搞懂了Size Class的知識以后,我們再看看Size Class變換的時候我們可以做些什么呢?
- 改變SubViews的尺寸和位置
- 添加或者移除subView
- 添加,移除或者修改約束(注:約束只能修改constant)
- 改變UILabel,TextField,Text view等的字體大小
## 五. Demo
我寫了一個適陪Spilt View多任務分屏的Demo,地址:SpiltViewDemo,App的內容很簡單,界面上只包含兩個元素一個ImageView用來展示圖片,一個UITextView用來展示文字描述。
截圖如下:

在Regular模式下圖文結構為左右結構,當進入到Compact模式時切換為上下結構。
核心的代碼如下:
#pragma mark -
#pragma mark Size Class Related
- (void)updateConstraintsForSizeClass:(UIUserInterfaceSizeClass)newSizeClass
{
NSArray *currentConstraints = [self constraintsForSizeClass:self.traitCollection.horizontalSizeClass];
NSArray *newConstraints = [self constraintsForSizeClass:newSizeClass];
[self.view removeConstraints:currentConstraints];
[self.view addConstraints:newConstraints];
if (newSizeClass == UIUserInterfaceSizeClassRegular) {
_imageIV.image = [UIImage imageNamed:@"aodi.jpg"];
_textView.font = [UIFont systemFontOfSize:24];
_textView.text = _aodiDes;
}
else {
_imageIV.image = [UIImage imageNamed:@"aotuo.jpg"];
_textView.font = [UIFont systemFontOfSize:16];
_textView.text = _aodiDes;
}
[self.view updateConstraintsIfNeeded];
}
- (void)willTransitionToTraitCollection:(UITraitCollection *)newCollection withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super willTransitionToTraitCollection:newCollection withTransitionCoordinator:coordinator];
[self updateConstraintsForSizeClass:newCollection.horizontalSizeClass];
}
可以看到只是簡單的在willTransitionToTraitCollection:withTransitionCoordinator:方法中檢測Size Class的變化,然后跟新約束系統即可。
當然實際應用在適配Spilt View的時候工作量可能遠比這個多,但是原理都是一樣的。
實際應用適配Spilt View除了技術上的支持,我想更多地是交互和視覺的調整,在多任務模式切換的時候如何更合理的調整元素的擺放位置以及交互的方式。
## 六. 總結 & 思考
1. 向下兼容
Size Class是iOS 8才引入的概念,如果你也像我一樣使用手寫的方式寫界面,且你的應用還需要支持iOS 7,那么需要小心行事。
如果你的應用使用Storyboard的方式使用Size Class,那么恭喜你只要你符合以下幾點要求,那么系統會自動幫你做向下兼容。
- 使用Xcode6及以后編譯
- 豎直方向的Size Class不是Compact模式
Important: Compatibility occurs at build time, not at run time.
2. 關于性能
在最后還是要提一下App的性能,作為一個iOS上的好公民,我們需要多花一些精力來做App的性能調優,用好Profile工具,仔細查找并優化App在CPU,GPU,memory,I/O等方面的占用。
3. 及時跟進iOS的新技術
及時跟進iOS的新技術,這樣在出現新特性的時候才能快速方便的接入。仔細想想從iOS 6的Auto Layout, iOS8 的Size Class,再到iOS 9推出的Spilt View,整個發展的遞進式的。
4. 參考資料
Adopting Multitasking Enhancements on iPad
Size Classes Design Help
Strategies for Handling App State Transitions
UITraitEnvironment
UIContentContainer
UITraitCollection
Building Adaptive Apps with UIKit
注:smileEvday保留本文的一切權利,轉載請著名原文出處
本文所有內容僅代表個人觀點,如有有不對的地方,歡迎指出。
??請樓主喝杯豆漿??
![]() |

浙公網安備 33010602011771號