蘋果剛發(fā)布了iOS9,在ipad上新增了兩個新的特性SlideView和SplitView,前者可以在不關(guān)閉當(dāng)前激活A(yù)PP的情況下調(diào)出來另外個APP以30%比例顯示進(jìn)行操作使用,后者允許同時運(yùn)行兩個APP以50%50%,70%30%比例運(yùn)行,感覺非常方便。
然而,方便了用戶的同時卻惡心了開發(fā)者,在同一屏幕運(yùn)行兩種APP的時候勢必APP顯示比例發(fā)生改變,那么就需要對幾種不同的大小進(jìn)行處理,好在蘋果有Autolayout,并且在iOS8中新增了SizeClass特性,兩者結(jié)合,可以很好的應(yīng)付以上各種情況。
好了,為了適配iOS9上述的特性,先來看下蘋果的文檔說明來如何處理多種顯示比例的問題。Adopting Multitasking Enhancements on iPad中對這種情況作了很好的描述,最主要的就是先理解一幅圖片。
  
在iOS8中新增的SizeClass能很好理解圖片的內(nèi)容,C(Compact)緊湊,R(Regular)常規(guī),通過C和R的組合可以匹配出各種屏幕,如果不理解最直觀的可以查看StoryBoard中設(shè)置Autolayout時候在底部出現(xiàn)的w Any h Any通過鼠標(biāo)移動可以得出各種組合后能適配哪一種屏幕,這里就不再闡述。
此圖可見,在iPad標(biāo)準(zhǔn)的屏幕比例種,width和height都是R,也就是說無論橫屏還是豎屏都是常規(guī)的組合即wR hR,然而在出現(xiàn)split和slide后狀態(tài)即發(fā)生的改變,在豎屏狀態(tài)下APP被分割后出現(xiàn)了wC hR,在橫屏狀態(tài)下,又出現(xiàn)了兩種組合分別是主APP70%從APP30%和主APP50%從APP50%,通過圖片得出在橫屏7:3中,主APP比例是wRhR,從APP比例是wChR,在5:5情況下主從APP都是wChR,那么就此知道了APP在slideView和splitView狀態(tài)下的各種高寬組合。
總結(jié)一下APP在Slide和Split后的各種需要適配的尺寸是,100%常規(guī)狀態(tài),70%作為主APP的狀態(tài),50%作為split等分的狀態(tài),30%作為從APP出現(xiàn)時候的狀態(tài)。由于100%和70%都屬于wRhR,那么我們主要適配就分成三中情況 100%,50%,30%,如果APP界面主要以list為主或者比較簡單的布局,其實(shí)只要適當(dāng)調(diào)整Autolayout的offset值即可適配所有的情況,那么如果是比較復(fù)雜的界面或者需要滿足各種狀態(tài)下的顯示怎么辦呢,當(dāng)然是有解決的方案,以下主要以簡單代碼的例子進(jìn)行適配工作,主要理解原理和知道什么時候觸發(fā)顯示比例改變,還有種方法是通過Storyboard的SizeClass匹配上述所有狀況并且逐一調(diào)整差值,這種方式比較簡單用慣XIB的應(yīng)該很容易解決,缺點(diǎn)就是維護(hù)起來稍微不方便。
首先,有個需求,在屏幕當(dāng)中放置一個紅色的UIView,在正常狀態(tài)下,左右兩邊距邊框100個像素,并且有個label顯示當(dāng)前的比例,當(dāng)出發(fā)split或者slide的時候,UIView的左右邊框調(diào)整為10個像素。具體的結(jié)果如下圖:
  

上圖為正常的全屏,下圖為splitView之后的。
首先代碼先在view內(nèi)增加一個紅色的UIView和一個label用于顯示當(dāng)前狀態(tài)。
1 var testingView:UIView! 2 var collectionStateLabel:UILabel! 3 4 testingView = UIView() 5 testingView.backgroundColor = UIColor.redColor() 6 self.view.addSubview(testingView) 7 8 collectionStateLabel = UILabel() 9 collectionStateLabel.textAlignment = NSTextAlignment.Center10 collectionStateLabel.textColor = UIColor.blackColor()11 self.view.addSubview(collectionStateLabel)
接著,開始分析實(shí)際情況,通過模擬器或者真機(jī)使用后就會發(fā)現(xiàn),在應(yīng)用程序啟動的時候就可能出現(xiàn)好幾種情況,紅色數(shù)字代表我的APP顯示比例
場景1 如果我正在瀏覽照片,這時候突然想打開APP查看某樣?xùn)|西的時候那么這時候就會發(fā)生幾種情況。
1 程序以SlideView啟動。 (10:3)
2 在通過SlideView啟動后又展開到了SplitView。 (5:5)
3 在SplitView使用后我覺得不爽,太小了,再想進(jìn)一步展開又成為全屏。 (0:10)
場景2 如果我正在使用APP,這時候我想通過地圖查看某幢樓在哪里,這時候又會發(fā)生幾種情況。
1 程序正在使用時,接受地圖程序以slide方式切入,此時地圖程序被激活,可以查詢地圖 (10:3)
2 在查詢地圖的時候我還要使用回我的APP,這時候我的APP被激活,地圖同樣被激活 (7:3)
3 我想放大地圖程序被地圖以SplitView切割成一半顯示。 (5:5)
從上總結(jié)出來,場景1我的APP是作為從APP存在,照片是主APP,場景2我的APP是作為主APP存在,地圖程序是從APP,其實(shí)這都不重要,最主要的得出結(jié)論就是在布局的時候一開始就必須考慮到針對不同的場景以適應(yīng)不同的布局需求。
現(xiàn)在開始代碼布局,代碼布局使用了一個第三方的類庫以節(jié)省代碼量,Apple的API實(shí)在非常的繁瑣,在此使用SnapKit作為布局類庫 (https://github.com/SnapKit/SnapKit),OC版本(https://github.com/SnapKit/Masonry)
從場景1,2分析出在一開始就需要知道當(dāng)前的屏幕處在什么樣的比例之中,那么通過文章一開始分析的Apple文檔得出在slide和split下的比例都是wC hR也就是說寬是緊湊豎是標(biāo)準(zhǔn)。那么通過SizeClass的API就可以判斷出來。
if self.traitCollection.verticalSizeClass == UIUserInterfaceSizeClass.Regular && self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact{ //slide or split size slide和split狀態(tài) }else{ //regular size 標(biāo)準(zhǔn)狀態(tài) }
通過 UITraitCollection 類可以獲取當(dāng)前屏幕處在什么樣子的比例當(dāng)中 ,這個類封裝了各種水平豎直方向等SizeClass的信息,通過實(shí)現(xiàn)了UITraitEnvironment接口的對象都可以拿到這個屬性,(UIViewController,UIView,UIWindow,UIScreen都實(shí)現(xiàn)了這個接口)。通過判斷屬性verticalSizeClass和horizontalSizeClass的各種組合即可很容易獲取到當(dāng)前屏幕水平垂直配比。
至此,屏幕顯示比例判斷出來了,那么就根據(jù)需求和實(shí)際情況分別編寫不同比例下的適配代碼即可,這里根據(jù)需求在regular下按鈕左右邊距100像素,在split下按鈕左右10個像素。
func setViewToRegularSize(){ collectionStateLabel.text = "State:Regular View" guard testingView.constraints.isEmpty else{ testingView.snp_updateConstraints { (make) -> Void in make.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) -> Void in make.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) -> Void in make.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) -> Void in make.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) } }
我們設(shè)置了兩個函數(shù)第一個函數(shù)適配regular的情況,第二個函數(shù)適配Split或Slide的情況,并且分別在label上標(biāo)注,這里使用了swift guard ... else {}來判斷如果約束不為空則更新約束,否則新增約束,關(guān)于snapkit用法具體請看GIT上說明,這里僅僅舉例。現(xiàn)在分別在初始化時候調(diào)用相應(yīng)的函數(shù)即可完成APP啟動時候的顯示適配。并且給label上好約束。
if traitCollection.verticalSizeClass == UIUserInterfaceSizeClass.Regular && self.traitCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact{ //slide or split size self.setViewToSlideSplitSize() }else{ //regular size self.setViewToRegularSize() } collectionStateLabel.snp_makeConstraints { (make) -> Void in make.top.equalTo(testingView.snp_bottom).offset(50) make.centerX.equalTo(testingView.snp_centerX) }
到了這一步已經(jīng)滿足了,APP從slide進(jìn)來時候的適配。但是,事情還沒那么簡單,通過場景1,2得出,在不滿足當(dāng)前大小的情況下可以從slide過渡到split,甚至從split過度到regular,那么就牽涉到動態(tài)更改布局了,好在Apple的API提供了一個函數(shù)來的到當(dāng)前sizeClass的改變。在viewcontroller中輸入以下代碼
override func willTransitionToTraitCollection(newCollection: UITraitCollection, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) { super.willTransitionToTraitCollection(newCollection, withTransitionCoordinator: coordinator) if newCollection.verticalSizeClass == UIUserInterfaceSizeClass.Regular && newCollection.horizontalSizeClass == UIUserInterfaceSizeClass.Compact{ self.setViewToSlideSplitSize() }else{ self.setViewToRegularSize() } }
這個函數(shù)類似于以前willRotateToInterfaceOrientation(toInterfaceOrientation: UIInterfaceOrientation, duration: NSTimeInterval)處理旋轉(zhuǎn)屏幕的邏輯一樣,當(dāng)sizeClass發(fā)生改變后立即會得到調(diào)用,那么在這個函數(shù)內(nèi)根據(jù)Apple文檔提供的slide和split的比例規(guī)則也非常容易對當(dāng)前布局進(jìn)行更新。
至此,我們已經(jīng)完全滿足了需求和場景1和2的各種情況,綜上所述,只要知道sizeClass的各種比例組合就可以輕松應(yīng)付各種屏幕顯示發(fā)生改變的情況,再通過與Autolayout的配合,達(dá)到滿足各種尺寸及動態(tài)改變尺寸需求。最后,雖然蘋果引入的新的特性,看似復(fù)雜,其實(shí)還是使用老的技術(shù)來解決各種情況,sizeClass和autolayout配合猶如雙劍合壁,無懼任何尺寸大小的變更。
題外話,如果習(xí)慣使用XIB的話用法還是和之前的一樣,只需要匹配各種Compact和Regular的組合并且設(shè)置好相應(yīng)的約束并且Install對應(yīng)的View也非常容易對付Slide和Split
  
只需要動動鼠標(biāo)修改下約束也很好的滿足實(shí)際的情況,只是storyboard的維護(hù)性和可讀性不是很友好,代碼可能會更容易做出修改維護(hù)和抽象,實(shí)際應(yīng)用我代碼使用的比較多點(diǎn)。
新聞熱點(diǎn)
疑難解答
圖片精選