iOS 9多任務(wù)分屏要點(diǎn)
iOS 9 多任務(wù)綜述
iOS 9 中最引人注目的新特性就是多任務(wù)了,在很久以前的越獄開(kāi)發(fā)里就已經(jīng)出現(xiàn)過(guò)類似的插件,而像是 Windows Surface 系列上也已經(jīng)有分屏多任務(wù)的特性,可以讓用戶同時(shí)使用兩個(gè)或多個(gè) app。iOS 9 中也新加入類似的特性。iOS 9 中的多任務(wù)有三種表現(xiàn)形式,臨時(shí)出現(xiàn)和交互的滑動(dòng)覆蓋 (Slide Over),真正的分屏同時(shí)操作兩個(gè) app 的分割視圖 (Split View),以及在其他 app 里依然可以進(jìn)行視頻播放的畫中畫 (Picture in Picture) 模式。
在關(guān)于多任務(wù)的文檔中,Apple 明確指出:
絕大部分 app 都應(yīng)當(dāng)適配 Slide Over 和 Split View
因?yàn)檫@正是 iOS 9 的核心功能之一,也是你的用戶所期望看到的。另一方面,支持多任務(wù)也增加了你的用戶打開(kāi)和使用你的 app 的可能。不過(guò)多任務(wù)有一點(diǎn)限制,那就是在能夠安裝 iOS 9 的 iPad 設(shè)備上,僅只有性能最強(qiáng)大的 iPad Air 2 和之后的機(jī)型支持分割視圖模式,而其他像是 iPad mini 2,iPad mini 3 以及 iPad Air 只支持滑動(dòng)覆蓋和畫中畫兩種模式。這在一定程度上應(yīng)該還是基于移動(dòng)設(shè)備資源和性能限制的考慮做出的決策,畢竟要保證良好的使用體驗(yàn)為前提,多任務(wù)才會(huì)有意義。
對(duì)于開(kāi)發(fā)者來(lái)說(shuō),雖然多種布局看起來(lái)很復(fù)雜,但是實(shí)際上如果緊跟 Apple 的技術(shù)步伐的話,將自己的 iPad app 進(jìn)行多任務(wù)適配并不會(huì)是一件非常困難的事情。因?yàn)榛瑒?dòng)覆蓋模式和分割視圖模式所采用的布局其實(shí)就是 Compact Width 的布局,而這一概念就是 WWDC14 上引入的基于屏幕特征的 UI 布局方式。如果你已經(jīng)在使用這套布局方式了的話,那么可以說(shuō)多任務(wù)視圖的支持也就順帶自動(dòng)完成了。不過(guò)如果你完全沒(méi)有使用過(guò)甚至沒(méi)有聽(tīng)說(shuō)過(guò)這套布局方法的話,我去年的一篇筆記可能能幫你對(duì)此有初步了解,在下一節(jié)里我也會(huì)稍微再稍微復(fù)習(xí)一下相關(guān)概念和基本用法。
Adaptive UI 復(fù)習(xí)
Adaptive UI 是 Apple 在 iOS 8 提出的概念。在此之前,我們?nèi)绻胍瑫r(shí)為 iPhone 和 iPad 開(kāi)發(fā) app 的話,很可能會(huì)寫很多設(shè)備判斷的代碼,比如這樣:
- if UI_USER_INTERFACE_IDIOM() == .Pad {
- // 設(shè)備是 iPad
- }
- 除此之外,如果我們想要同時(shí)適配橫向和縱向的話,我們會(huì)需要類似這樣的代碼:
- if UIInterfaceOrientationIsPortrait(orientation) {
- // 屏幕是豎屏
- }
這些判斷和分支不僅難寫難讀,也使適配開(kāi)發(fā)困難重重。從 iOS 8 之后,開(kāi)發(fā)者不應(yīng)該再依賴這樣設(shè)備向來(lái)進(jìn)行 UI 適配,而應(yīng)該轉(zhuǎn)而使用新的 Size Class 體系。Apple 將自家的移動(dòng)設(shè)備按照尺寸區(qū)別,將縱橫兩個(gè)方向設(shè)計(jì)了 Regular 和 Compact 的組合。比如 iPhone 在豎屏?xí)r寬度是 Compact,高度是 Regular,橫屏?xí)r iPhone 6 Plus 寬度是 Regular,高度是 Compact,而其他 iPhone 在橫屏?xí)r高度和寬度都是 Compact;iPad 不論型號(hào)和方向,寬度及高度都是 Regular。現(xiàn)有的設(shè)備的 Size Class 如下圖所示:
針對(duì) Size Class 進(jìn)行開(kāi)發(fā)的思想下,我們不再關(guān)心具體設(shè)備的型號(hào)或者尺寸,而是根據(jù)特定的 Size Class 的特性來(lái)展示內(nèi)容。在 Regular 的寬度下,我們可以在水平方向上展示更多的內(nèi)容,比如同時(shí)顯示 Master 和 Detail View Controller 等。同樣地,我們也不應(yīng)該再關(guān)心設(shè)備旋轉(zhuǎn)的問(wèn)題,而是轉(zhuǎn)而關(guān)心 Size Class 的變化。在開(kāi)發(fā)時(shí),如果是使用 Interface Builder 的話,在制作 UI 時(shí)就注意為不同的 Size Class 配置合適的約束和布局,在大多數(shù)情況下就已經(jīng)足夠了。如果使用代碼的話,UITraitCollection 類將是使用和操作 Size Class 的關(guān)鍵。我們可以根據(jù)當(dāng)前工作的 UIViewController 的 traitCollection 屬性來(lái)設(shè)置合適的布局,并且在 -willTransitionToTraitCollection:withTransitionCoordinator: 和 -viewWillTransitionToSize:withTransitionCoordinator: 被調(diào)用時(shí)對(duì) UI 布局做出正確的響應(yīng)。
雖然并不是理論上不可行,但是使用純手寫來(lái)操作 Size Class 會(huì)是一件異常痛苦的事情,我們還是應(yīng)該盡可能地使用 IB 來(lái)減少這部分的工作量,加快開(kāi)發(fā)效率。
iPad 中的多任務(wù)適配
對(duì)于 iOS 9 中的多任務(wù),滑動(dòng)覆蓋和分割視圖的初始位置,新打開(kāi)的 app 的尺寸都將是設(shè)備尺寸的 1/3。不過(guò)這個(gè)比例并不重要,我們需要記住的是新打開(kāi)的 app 將運(yùn)行在 Compact Width 和 Regular Height 的 Size Class 下。也就是說(shuō),如果你的 iPad app 使用了 Size Class 進(jìn)行布局,并且是支持 iPhone 豎屏的,那么恭喜,你只需要換到 iOS 9 SDK 并且重新編譯你的 app,就搞定了。
因?yàn)楸疚牡闹攸c(diǎn)不是教你怎么開(kāi)發(fā)一個(gè) Adaptive UI 的 app,所以并不打算在這方面深入下去。如果你在去年缺了課,不是很了解這方面的話,這篇教程可能可以幫你快速了解并掌握這些內(nèi)容。如果你想要直接上手看看 iOS 9 中的 多任務(wù)是如何工作的話,可以新建一個(gè) Master-Detail Application,并將其安裝到 iPad 模擬器上。Master-Detail 的模板工程為我們搭設(shè)了一個(gè)很好的適配 Size Class 的框架,讓項(xiàng)目可以在任何設(shè)備上都表現(xiàn)良好。同樣你也可以觀察它在 iOS 9 的 iPad 上的表現(xiàn)。
但是其實(shí)并不是所有的 app 都應(yīng)該適配多任務(wù),比如一個(gè)需要全屏才能體驗(yàn)的游戲就是典型。如果你不想你的 app 可以作為多任務(wù)的副 app 被使用的話,你可以在 Info.plist 中添加 UIRequiresFullScreen 并將其設(shè)為 true。
Easy enough?沒(méi)錯(cuò),要適配 iPad 的多任務(wù),你需要做的就只有按照標(biāo)準(zhǔn)流程開(kāi)發(fā)一個(gè)全平臺(tái)通用 app,僅此而已。
使用 iOS 9 SDK 構(gòu)建你的 app;
支持所有的方向和對(duì)應(yīng)的 Size Class;
使用 launch storyboard 作為 app 啟動(dòng)頁(yè)面。
雖說(shuō)沒(méi)太多特別值得一提的內(nèi)容,但是也還是有一些需要注意的小細(xì)節(jié)。
一些值得注意的小細(xì)節(jié)
在以前是不存在 app 在前臺(tái)還要和別的 app 共享屏幕這種事情的,所以 UIScreen.bounds 和主窗口的 UIWindow.bounds 使用上來(lái)說(shuō)基本是同義詞。但是在多任務(wù)時(shí)代,UIWindow 就有可能只有 1/3 或者 1/2 屏幕大小了。如果你在之前的 app 中有使用它來(lái)定義你的視圖的話,就有必要為多任務(wù)做特殊的處理了。不過(guò)雖然滑動(dòng)覆蓋和分割視圖都是在右側(cè)展示,但是它們的 Window 的 origin 依然是 (0, 0),這也方便了我們定義視圖。
第二個(gè)細(xì)節(jié)是現(xiàn)在 iPad UI 的 Size Class 是會(huì)發(fā)生變化的。以前不論是豎直還是水平,iPad 屏幕的 Size 總是長(zhǎng)寬均為 Regular 的。但是在 iOS 9 中情況就不一樣了,你的 app 可能被作為附加 app 通過(guò)多任務(wù)模式打開(kāi),可能會(huì)在多任務(wù)時(shí)被用戶拖動(dòng)從而變成全屏 app (這時(shí) Size Class 將從 Compact 的寬度變?yōu)?Regular),甚至可能你的 app 作為主 app 被使用是會(huì)因?yàn)橛脩敉蟿?dòng)而變成 Compact 寬度的 app:
換句話說(shuō),你不知道你的 app 的 Size Class 會(huì)不會(huì)變化,以及何時(shí)變化,這都是用戶操作的結(jié)果。因此在開(kāi)發(fā)時(shí),就必須充分考慮到這一點(diǎn),力求在尺寸變化時(shí)呈現(xiàn)給用戶良好的效果。根據(jù)屏幕大小進(jìn)行合適的 UI 設(shè)計(jì)和調(diào)整自不用說(shuō),另外還應(yīng)當(dāng)注意在合適的時(shí)機(jī)利用 transitionCoordinator 的 -animateAlongsideTransition: 來(lái)進(jìn)行布局動(dòng)畫,讓切換更加自然。
由于多任務(wù)帶來(lái)了多個(gè) app 同臺(tái)運(yùn)行的可能性,因此你的 app 必定會(huì)面臨和別的 app 一起運(yùn)行的情況。在開(kāi)發(fā)移動(dòng)應(yīng)用時(shí)永遠(yuǎn)不能忘記的是設(shè)備平臺(tái)的限制。相比于桌面設(shè)備,移動(dòng)端只有有限的內(nèi)存,而兩個(gè)甚至三個(gè) app 同時(shí)在前臺(tái)運(yùn)行,就需要我們精心設(shè)計(jì)內(nèi)存的使用。對(duì)于一般開(kāi)發(fā)者來(lái)說(shuō),合理地分配內(nèi)存,監(jiān)聽(tīng) Memory Warning 來(lái)釋放 cache 和不必要的 view controller,避免循環(huán)引用等等,應(yīng)該成為熟練掌握的日常開(kāi)發(fā)基本功。
最后一個(gè)細(xì)節(jié)是對(duì)完美的苛求了。在 iOS 9 中多任務(wù)也通過(guò) App Switcher 來(lái)進(jìn)行 app 之間的切換的。所以在你的 app 被切換到后臺(tái)時(shí),系統(tǒng)會(huì)保存你的 app 的當(dāng)前狀態(tài)的截圖,以供之后切換時(shí)顯示。你的 app 現(xiàn)在有可能被作為 Regular 的全屏 app 使用,也可能使用 Compact 布局,所以在截圖時(shí)系統(tǒng)也會(huì)依次保存兩份截圖。用戶可能會(huì)在全屏模式下把你的 app 關(guān)閉,然后通過(guò)多任務(wù)再將你的 app 作為附加 app 打開(kāi),這時(shí)最好能保證 App Switcher 中的截圖和 app 打開(kāi)后用戶看到的截圖一致,以獲取最好的體驗(yàn)??赡苓@并不是一個(gè)很大的問(wèn)題,但是如果追求極致的用戶體驗(yàn)的話,這也是必行的。對(duì)于那些含有用戶敏感數(shù)據(jù),需要將截圖模糊處理的 app,現(xiàn)在也需要注意同時(shí)將兩種布局的截圖都進(jìn)行處理。
畫中畫模式
iOS 9 中多任務(wù)的另一種表現(xiàn)形式就是視頻的畫中畫模式:即使退出了,你的視頻 app 也可以在用戶使用別的 app 的時(shí)候保持播放,比如一邊看美劇一邊寫日記或者發(fā)郵件。這大概會(huì)是所有的視頻類 app 都必須要支持的特性了,實(shí)現(xiàn)起來(lái)也很容易:
使用 iOS 9 SDK 構(gòu)建你的 app;
在 app 的 Capabilities 里,將 Background Modes 的 "Audio, AirPlay, and Picture in Picture" 勾選上 (Xcode 7 beta 中暫時(shí)為 "Audio and AirPlay");
將 AudioSession Catogory 設(shè)置為合適的選項(xiàng),比如 AVAudioSessionCategoryPlayback
使用 AVKit,AVFoundation 或者 WebKit 框架來(lái)播放視頻。
在 iOS 9 中,一直伴隨我們的 MediaPlayer 框架中的視頻播放部分正式宣布?jí)劢K正寢。也就是說(shuō),如果你在使用 MPMoviePlayerViewController 或者 MPMoviePlayerController 在播放視頻的話,你就無(wú)法使用畫中畫的特性了,因此盡快轉(zhuǎn)型到新的視頻播放框架會(huì)是急迫的適配任務(wù)。因?yàn)楫嬛挟嬆J绞腔?AVPlayerLayer 的。當(dāng)切換到畫中畫時(shí),會(huì)將正在播放視頻的 layer 取出,然后進(jìn)行縮小后添加到新的界面的 layer 上。這也是舊的 MediaPlayer 框架無(wú)法支持畫中畫的主要原因。
如果你使用 AVPlayerViewController 的話,一旦滿足這些簡(jiǎn)單的條件以后,你應(yīng)該就可以在使用相應(yīng)框架全屏播放視頻時(shí)看到右下角的畫中畫按鈕了。不論是點(diǎn)擊這個(gè)按鈕進(jìn)入畫中畫模式還是直接使用 Home 鍵切換到后臺(tái),已經(jīng)在播放的視頻就將縮小到屏幕右下角成為畫中畫,并保持播放。在畫中畫模式下,系統(tǒng)會(huì)在視頻的 AVPlayerLayer 上添加一套默認(rèn)控件,用來(lái)控制暫停/繼續(xù),關(guān)閉,以及返回 app。前兩個(gè)控制沒(méi)什么可多說(shuō)的,返回 app 的話需要我們自己處理返回后的操作。一般來(lái)說(shuō)我們希望能夠恢復(fù)到全屏模式并且繼續(xù)播放這個(gè)視頻,因?yàn)?AVPlayerViewController 進(jìn)行播放時(shí)我們一般不會(huì)去操作 AVPlayerLayer,在恢復(fù)時(shí)就需要實(shí)現(xiàn) AVPlayerViewControllerDelegate 中的 -playerViewController:restoreUserInterfaceForPictureInPictureStopWithCompletionHandler: 來(lái)根據(jù)傳入的 ViewController 重建 UI,并將 true 通過(guò) CompletionHandler 返回給系統(tǒng),已告知系統(tǒng)恢復(fù)成功 (當(dāng)然如果無(wú)法恢復(fù)的話需要傳遞 false)。
我們也可以直接用 AVPlayerLayer 來(lái)構(gòu)建的自定義的播放器。這時(shí)我們需要通過(guò)傳入所使用的 AVPlayerLayer 來(lái)創(chuàng)建一個(gè) AVPictureInPictureController。AVPictureInPictureController 提供了檢查是否支持畫中畫模式的 API,以及其他一些控制畫中畫行為的方法。與直接使用 AVPlayerViewController 不太一樣的是,在恢復(fù)時(shí),系統(tǒng)將會(huì)把畫中畫時(shí)縮小的 AVPlayerLayer 返還到之前的 view 上。我們可以通過(guò) AVPictureInPictureControllerDelegate 中的相應(yīng)方法來(lái)獲知畫中畫的執(zhí)行情況,并結(jié)合自己 app 的情況來(lái)恢復(fù) UI。
總結(jié)
通過(guò)之前幾年的布局,在 AutoLayout 和 Size Class 的基礎(chǔ)上,Apple 在 iOS 9 中放出了多任務(wù)這一殺手锏??梢哉f(shuō)同屏執(zhí)行多個(gè) app 的需求從初代 iPad 開(kāi)始就一直存在,而現(xiàn)在總算是姍姍來(lái)遲。在 OS X 10.11 中,Apple 也將類似的特性引入了 OSX app 的全屏模式中,可以說(shuō)是統(tǒng)一 OSX 和 iOS 這兩個(gè)平臺(tái)的進(jìn)一步嘗試。
但是 iPad 上的多任務(wù)還是有一些不足的。最大的問(wèn)題是 app 依然是運(yùn)行在沙盒中的,這就意味著在 iOS 上我們還是無(wú)法在兩個(gè) app 之間進(jìn)行通訊:比如同時(shí)打開(kāi)照片和一個(gè)筆記 app,我們無(wú)法通過(guò)拖拽方式將某張圖片直接拖到筆記中去。雖然在 iOS 中也有 XPC 服務(wù),但是第三方開(kāi)發(fā)者現(xiàn)在并不能使用,這在一定程度上還是限制了多任務(wù)的可能性。
不過(guò)總體來(lái)說(shuō),多任務(wù)特性使得 iPad 的實(shí)用性大大上升,這也肯定會(huì)是未來(lái)用戶最常用以及最希望在 app 中看到的特性之一?;ㄒ稽c(diǎn)時(shí)間,學(xué)習(xí) Adaptive UI 的制作方式,讓 app 支持多任務(wù)運(yùn)行,會(huì)是一件很合算的事情。