初識iOS9 iPad新特性SlideView和SplitView的適配


  蘋果剛發布了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用於顯示當前狀態。

 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.Center
10         collectionStateLabel.textColor = UIColor.blackColor()
11         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就可以判斷出來。

     if self.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個像素。

  

    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)
        }
    }

  我們設置了兩個函數第一個函數適配regular的情況,第二個函數適配Split或Slide的情況,並且分別在label上標注,這里使用了swift guard ... else {}來判斷如果約束不為空則更新約束,否則新增約束,關於snapkit用法具體請看GIT上說明,這里僅僅舉例。現在分別在初始化時候調用相應的函數即可完成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)
        }

  到了這一步已經滿足了,APP從slide進來時候的適配。但是,事情還沒那么簡單,通過場景1,2得出,在不滿足當前大小的情況下可以從slide過渡到split,甚至從split過度到regular,那么就牽涉到動態更改布局了,好在Apple的API提供了一個函數來的到當前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()
        }
    }

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

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

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

  

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM