1. 程式人生 > >初識 iOS9 iPad 新特性 SlideView 及其的適配

初識 iOS9 iPad 新特性 SlideView 及其的適配

蘋果剛釋出了iOS9,在iPad上新增了兩個新的特性SlideView和SplitView,前者可以在不關閉當前啟用APP的情況下調出來另外個APP以30%比例顯示進行操作使用,後者允許同時執行兩個APP以50%50%,70%30%比例執行,感覺非常方便。

然而,方便了使用者的同時卻噁心了開發者,在同一螢幕執行兩種APP的時候勢必APP顯示比例發生改變,那麼就需要對幾種不同的大小進行處理,好在蘋果有Autolayout,並且在iOS8中新增了SizeClass特性,兩者結合,可以很好的應付以上各種情況。

好了,為了適配iOS9上述的特性,先來看下蘋果的文件說明來如何處理多種顯示比例的問題。Adopting Multitasking Enhancements on iPad中對這種情況作了很好的描述,最主要的就是先理解一幅圖片。

在iOS8中新增的SizeClass能很好理解圖片的內容,C(Compact)緊湊,R(Regular)常規,通過C和R的組合可以匹配出各種螢幕,如果不理解最直觀的可以檢視StoryBoard中設定Autolayout時候在底部出現的w Any h Any通過滑鼠移動可以得出各種組合後能適配哪一種螢幕,這裡就不再闡述。

此圖可見,在iPad標準的螢幕比例種,width和height都是R,也就是說無論橫屏還是豎屏都是常規的組合即wR hR,然而在出現split和slide後狀態即發生的改變,在豎屏狀態下APP被分割後出現了wC hR,在橫屏狀態下,又出現了兩種組合分別是主APP70%從APP30%和主APP50%從APP50%,通過圖片得出在橫屏7:3中,主APP比例是wRhR,從APP比例是wChR,在5:5情況下主從APP都是wChR,那麼就此知道了APP在slideView和splitView狀態下的各種高寬組合。

總結一下APP在Slide和Split後的各種需要適配的尺寸是,100%常規狀態,70%作為主APP的狀態,50%作為split等分的狀態,30%作為從APP出現時候的狀態。由於100%和70%都屬於wRhR,那麼我們主要適配就分成三中情況 100%,50%,30%,如果APP介面主要以list為主或者比較簡單的佈局,其實只要適當調整Autolayout的offset值即可適配所有的情況,那麼如果是比較複雜的介面或者需要滿足各種狀態下的顯示怎麼辦呢,當然是有解決的方案,以下主要以簡單程式碼的例子進行適配工作,主要理解原理和知道什麼時候觸發顯示比例改變,還有種方法是通過Storyboard的SizeClass匹配上述所有狀況並且逐一調整差值,這種方式比較簡單用慣XIB的應該很容易解決,缺點就是維護起來稍微不方便。

首先,有個需求,在螢幕當中放置一個紅色的UIView,在正常狀態下,左右兩邊距邊框100個畫素,並且有個label顯示當前的比例,當出發split或者slide的時候,UIView的左右邊框調整為10個畫素。具體的結果如下圖:

上圖為正常的全屏,下圖為splitView之後的。

首先程式碼先在view內增加一個紅色的UIView和一個label用於顯示當前狀態。

Objective-C
1234567891011 vartestingView:UIView!    varcollectionStateLabel:UILabel!testingView=UIView()testingView.backgroundColor=UIColor.redColor()self.view.addSubview(testingView)collectionStateLabel=UILabel()collectionStateLabel.textAlignment=NSTextAlignment.CentercollectionStateLabel.textColor=UIColor.blackColor()self.view.addSubview(collectionStateLabel)

接著,開始分析實際情況,通過模擬器或者真機使用後就會發現,在應用程式啟動的時候就可能出現好幾種情況,紅色數字代表我的APP顯示比例

場景1 如果我正在瀏覽照片,這時候突然想開啟APP檢視某樣東西的時候那麼這時候就會發生幾種情況。

1 程式以SlideView啟動。                                 (10:3)

2 在通過SlideView啟動後又展開到了SplitView。                      (5:5)

3 在SplitView使用後我覺得不爽,太小了,再想進一步展開又成為全屏。            (0:10)

場景2 如果我正在使用APP,這時候我想通過地圖檢視某幢樓在哪裡,這時候又會發生幾種情況。

1 程式正在使用時,接受地圖程式以slide方式切入,此時地圖程式被啟用,可以查詢地圖      (10:3)

2 在查詢地圖的時候我還要使用回我的APP,這時候我的APP被啟用,地圖同樣被啟用        (7:3)

3 我想放大地圖程式被地圖以SplitView切割成一半顯示。                    (5:5)

從上總結出來,場景1我的APP是作為從APP存在,照片是主APP,場景2我的APP是作為主APP存在,地圖程式是從APP,其實這都不重要,最主要的得出結論就是在佈局的時候一開始就必須考慮到針對不同的場景以適應不同的佈局需求。

現在開始程式碼佈局,程式碼佈局使用了一個第三方的類庫以節省程式碼量,Apple的API實在非常的繁瑣,在此使用SnapKit作為佈局類庫 (https://github.com/SnapKit/SnapKit),OC版本(https://github.com/SnapKit/Masonry)

從場景1,2分析出在一開始就需要知道當前的螢幕處在什麼樣的比例之中,那麼通過文章一開始分析的Apple文件得出在slide和split下的比例都是wC hR也就是說寬是緊湊豎是標準。那麼通過SizeClass的API就可以判斷出來。

Objective-C
12345 ifself.traitCollection.verticalSizeClass==UIUserInterfaceSizeClass.Regular&&self.traitCollection.horizontalSizeClass==UIUserInterfaceSizeClass.Compact{//slide or split size slide和split狀態   }else{//regular size 標準狀態}

通過 UITraitCollection 類可以獲取當前螢幕處在什麼樣子的比例當中 ,這個類封裝了各種水平豎直方向等SizeClass的資訊,通過實現了UITraitEnvironment介面的物件都可以拿到這個屬性,(UIViewController,UIView,UIWindow,UIScreen都實現了這個介面)。通過判斷屬性verticalSizeClass和horizontalSizeClass的各種組合即可很容易獲取到當前螢幕水平垂直配比。

至此,螢幕顯示比例判斷出來了,那麼就根據需求和實際情況分別編寫不同比例下的適配程式碼即可,這裡根據需求在regular下按鈕左右邊距100畫素,在split下按鈕左右10個畫素。

Objective-C
1234567891011121314151617181920212223242526272829303132333435 func setViewToRegularSize(){collectionStateLabel.text="State:Regular View"guard testingView.constraints.isEmpty else{testingView.snp_updateConstraints{(make)->Voidinmake.left.equalTo(self.view.snp_left).offset(100)make.right.equalTo(self.view.snp_right).offset(-100)make.centerY.equalTo(self.view.snp_centerY)}return}testingView.snp_makeConstraints{(make)->Voidinmake.left.equalTo(self.view.snp_left).offset(100)make.right.equalTo(self.view.snp_right).offset(-100)make.centerY.equalTo(self.view.snp_centerY)make.height.equalTo(60)}}func setViewToSlideSplitSize(){collectionStateLabel.text="State:SplitView or SlideView"guard testingView.constraints.isEmpty else{testingView.snp_updateConstraints(closure:{(make)->Voidinmake.left.equalTo(self.view.snp_left).offset(10)make.right.equalTo(self.view.snp_right).offset(-10)make.centerY.equalTo(self.view.snp_centerY)})return}testingView.snp_makeConstraints{(make)->Voidinmake.left.equalTo(self.view.snp_left).offset(10)make.right.equalTo(self.view.snp_right).offset(-10)make.centerY.equalTo(self.view.snp_centerY)make.height.equalTo(60)}}

我們設定了兩個函式第一個函式適配regular的情況,第二個函式適配Split或Slide的情況,並且分別在label上標註,這裡使用了swift guard … else {}來判斷如果約束不為空則更新約束,否則新增約束,關於snapkit用法具體請看GIT上說明,這裡僅僅舉例。現在分別在初始化時候呼叫相應的函式即可完成APP啟動時候的顯示適配。並且給label上好約束。

Objective-C
1234567891011 iftraitCollection.verticalSizeClass==UIUserInterfaceSizeClass.Regular&&self.traitCollection.horizontalSizeClass==UIUserInterfaceSizeClass.Compact{//slide or split sizeself.setViewToSlideSplitSize()}else{//regular sizeself.setViewToRegularSize()}collectionStateLabel.snp_makeConstraints{(make)->Voidinmake.top.equalTo(testingView.snp_bottom).offset(50)make.centerX.equalTo(testingView.snp_centerX)}

到了這一步已經滿足了,APP從slide進來時候的適配。但是,事情還沒那麼簡單,通過場景1,2得出,在不滿足當前大小的情況下可以從slide過渡到split,甚至從split過度到regular,那麼就牽涉到動態更改佈局了,好在Apple的API提供了一個函式來的到當前sizeClass的改變。在viewcontroller中輸入以下程式碼

Objective-C
12345678 override func willTransitionToTraitCollection(newCollection: UITraitCollection,withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator){super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator)ifnewCollection.verticalSizeClass==UIUserInterfaceSizeClass.Regular&&newCollection.horizontalSizeClass==UIUserInterfaceSizeClass.Compact{self.setViewToSlideSplitSize()}else{self.setViewToRegularSize()}}

這個函式類似於以前willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval)處理旋轉螢幕的邏輯一樣,當sizeClass發生改變後立即會得到呼叫,那麼在這個函式內根據Apple文件提供的slide和split的比例規則也非常容易對當前佈局進行更新。

至此,我們已經完全滿足了需求和場景1和2的各種情況,綜上所述,只要知道sizeClass的各種比例組合就可以輕鬆應付各種螢幕顯示發生改變的情況,再通過與Autolayout的配合,達到滿足各種尺寸及動態改變尺寸需求。最後,雖然蘋果引入的新的特性,看似複雜,其實還是使用老的技術來解決各種情況,sizeClass和autolayout配合猶如雙劍合壁,無懼任何尺寸大小的變更。

題外話,如果習慣使用XIB的話用法還是和之前的一樣,只需要匹配各種Compact和Regular的組合並且設定好相應的約束並且Install對應的View也非常容易對付Slide和Split

只需要動動滑鼠修改下約束也很好的滿足實際的情況,只是storyboard的維護性和可讀性不是很友好,程式碼可能會更容易做出修改維護和抽象,實際應用我程式碼使用的比較多點。