AsyncDisplayKit介紹(二)佈局系統

在上一篇介紹中咱們曾經討論過Autolayout的性能問題。然而在iOS中,除了Autolayout,能選擇的只有autoresizingMask,或者純手動佈局。在寫了無數view.frame = CGRect(…)以後,咱們才發現,一個在HTML中很是簡單的流式佈局,到iOS9纔有相應的UIStackView予以支持。儘管你可使用 FDStackView 將系統要求下降到iOS6,對於複雜的佈局仍然須要多層View嵌套來實現——其實對於實現stack佈局的時候,並不須要一個UIView實例來承載全部的元素,只須要它的佈局功能而已。javascript

作過Web開發的朋友已經習慣了高效的css佈局:聲明式、易於調試,boxModel結構化清晰等等優勢,特別是使用自動化工具以後,只須要保存文件就能夠當即在瀏覽器中看到更新後的效果;而這一切在iOS中就變得高不可攀:命令式賦值、編譯後(Objc速度尚可,而swift編譯速度較慢)才能看到結果、Autolayout閉源、視圖調試複雜、xcode常常crash等等。若是要實現一個高性能的tableView,手寫佈局幾乎是惟一選擇。css

Layout的本質

在決定使用哪一種佈局方式以前,先看看設計師是怎麼思考佈局的:前端

  1. 每一個元素的大小(size)
  2. 兄弟元素的對齊方式:上下左右對齊、居中對齊(alignment)和間距(spacing);父子元素之間的容納關係:子元素的邊距(insets)

設計師最終肯定的,是一個佈局規則。java

設計稿到了工程師這邊之後,絕大多數時間都在作如下兩件事:node

  1. 肯定UIView自身的大小(覆蓋sizeThatFits方法),與parent view無關
  2. 根據規則,將subview放在父parent內的指定位置(對應layoutSubviews),由parent view決定

這樣層層遞歸,最終完成整個屏幕內全部子元素的佈局。工程師完成的,是一個基於佈局規則的具體實現。ios

也就是說,手動佈局或者經過約束(Autolayout)實際上是在『實現』佈局規則,而不是『聲明』佈局規則。抽象層級降低了,隨之而來的是多到使人頭疼的frame、size、origin計算,可讀性、可維護性都很是差。那能不能用一個數據結構來表示佈局規則,提升抽象層級,從而擺脫繁重又容易出錯的數值計算,讓佈局系統根據規則自動地、異步地計算呢?git

AsyncDisplayKit的三種佈局方式

1. 手動佈局

這是最基礎的一種佈局方式。在ASDK中的手動佈局與UIKit相似,只是方法名從sizeThatFits變成了calculatedSizeThatFits,layoutSubviews方法變成了layout方法。略微不一樣的是ASDK會將佈局結果異步預先計算,並緩存下來以提高性能,這點對於tableView滾動性能來講有很是大的幫助。github

然而,跟普通手動佈局相似,最大的缺點是可讀性和可維護性差。因爲自身的大小經常和子元素的佈局相關聯,這兩個方法中的代碼容易重複;同時因爲佈局針對自身subView,很難將其代碼與其餘View進行復用。swift

2. Unified layout

這也是在ASDK中新出現的概念。先介紹一下ASLayout:xcode

一個ASLayout對象包含如下元素:

  • 它所表明的佈局元素
  • 元素的尺寸
  • 元素的位置
  • 它所包含的sublayouts

能夠看出,當一個node具有了肯定的ASLayout對象時,它自身的佈局也就隨之肯定了。爲了生成ASLayout,ASDisplayNode爲subclass提供了以下覆蓋點:

- (ASLayout *)calculateLayoutThatFits:(ASSizeRange)constrainedSize複製代碼

只要Node可以計算出本身的ASLayout,父元素就能夠完成對其的佈局。這種方法將sizeThatFits和layoutSubviews結合在一塊兒,必定程度上避免了類似代碼的尷尬,可是計算上仍然是手動佈局,不夠簡便。

3. Automatic Layout(不是Autolayout)

有了ASLayout這一層抽象,再來看如何聲明規則來生成它。這也是最艱鉅的部分。

Facebook將React的概念延伸到native,同時也把聲明式的語法帶到了iOS,產生了ComponentKit框架。早在ASDK1.0的年代你們紛紛表示但願能將其與ComponentKit相融合,幸運的是,在2.0版本中實現了。我選用了做者Scott
Goodson在NSSpain的演講pdf做爲插圖,方便你們瞭解新的佈局系統。

所以Automatic Layout是ASDK最推薦也是最爲高效的佈局方式。它引入了ASLayoutSpec的概念,能夠理解爲一個抽象的容器(並不須要建立對應node或者view來裝載子元素),只須要爲它制定一系列的佈局規則,它就能對其負責的子元素進行佈局。

咱們先來看一下它們之間的關係:

能夠看到ASLayoutSpec和ASDisplayNode都實現了ASLayoutable接口,所以他們都具有生成ASLayout的能力,這樣就能惟一肯定自身的大小。

  • 對於以上提到的Unified佈局,當咱們實現了calculateLayoutThatFits,ASDK會在佈局過程當中調用measureWithSizeRange(若是沒有緩存過再調用calculateLayoutThatFits)來算出ASLayout。
  • 若是ASDisplayNode選擇實現layoutSpecThatFits,由更爲抽象的ASLayoutSpec來負責指定佈局規則,因爲ASLayoutSpec也實現了ASLayoutable接口,一樣也能夠經過調用measureWithSizeRange來得到ASLayout。

因爲ASLayoutSpec只負責指定佈局規則,而不關心其佈局的ASLayoutable具體是Node仍是其餘ASLayoutSpec,咱們能夠輕鬆地將ASLayoutSpec生成邏輯獨立出來,達到複用的目的。

挑大樑的ASStackLayoutSpec

ASLayoutSpec主要有如下幾種:

經過它們的命名就能夠了解其大體做用。其中用途最普遍的無疑是ASStackLayoutSpec,與UIStackView有殊途同歸之妙。

ASStackLayoutSpec大量借鑑了CSS的FlexBox概念,寫過Web代碼的同窗應該能一眼認出許多熟悉的屬性:justifyContent/flexDirection/alignSelf/alignItems/flexBasis等等。不熟悉的朋友也能夠經過一個有趣的遊戲來學習flexbox:flexboxfroggy.com/

示例

(圖中的Huy Nguyen也是ASDK佈局的主要貢獻者之一)

圖中左邊是一個imageNode,右邊是由兩個textNode組成的一個vertical
stack,再和imageNode組合成一個橫向的stack,最後加上邊緣inset完成整個佈局。

事實上,與css相似,絕大多數的佈局均可以經過stack和inset的組合來完成;而和UIStackView不一樣的是,layout spec只是一個存在於內存之中的數據結構,並不須要額外建立view容器來承載子元素,大大節約了複雜佈局帶來的開銷;同時由於它的輕量級和獨立性,所以可以將佈局規則放到後臺線程獨立計算,並緩存在node之中,對於大量tableView/collectionView的CellNode快速佈局有至關大的幫助。

Huy Nguyen也爲ASLayoutSpec寫了一個寓教於樂的小遊戲,只是將上面提到的的針對css的小遊戲版本改爲了Objc,方便你們快速學習。

在最近的版本中,ASDK也同時支持 Yoga (一個跨平臺的flexbox引擎,使用C語言實現)來計算基於flexbox的佈局,與ASDK自己實現的flexbox概念相似。

即刻的實踐

即刻在多個消息頁面使用了ASDK新的佈局系統,例如:

從咱們的實踐經驗來看有如下優缺點:

優勢

  • 實現一個layout的視角從專一view之間的constraint轉變成對定義多個view子區域的劃分,抽象層級變高,可讀性大大加強。
  • 因爲ASDisplayNode的顯示是異步的,所以不管佈局是否依賴於顯示結果,均可以在主線程之外進行,而且有緩存,性能有很大提高。
  • 借用css成熟的flexbox佈局模型,有大量現成資料和案例來學習,避免了新造輪子的尷尬。
  • 因爲ASDK是開源的,調試難度大大下降。
  • 佈局規則能夠脫離實際佈局對象進行獨立聲明,容易複用。

缺點

  • 因爲與iOS原生布局方式徹底不一樣,學習適應須要必定時間(固然若是flexBox漸漸成爲前端標準,花一些時間瞭解也是徹底值得的。)
  • 對於ASDK依賴很是重,對於須要使用flexBox佈局的view,只能從新使用ASDisplayNode來實現。若是不方便從新寫,只能選用相似Yoga的獨立框架來實現UIView的流式佈局。
  • 雖然是聲明式佈局,然而相對css而言仍較爲繁瑣。去年我曾經到Pinterest與Scott有過一些當面交流,也嘗試說服他們作一些DSL來簡化佈局聲明,可是目前因爲ASDK的複雜性和基礎性(相似UIKit),他們仍然將大部分時間放在優化異步渲染和佈局性能,並無太多精力在layout的語法上作出進一步突破。

雖然咱們沒有在整個項目都採用新的的佈局方式,然而ASDK帶來的啓發是深遠的:聲明式、異步佈局、flexBox、緩存等等。咱們也看到有一些其餘佈局庫在聲明式佈局作許多的嘗試,如Brickkit, Layoutkit等等,或許這就會是未來佈局系統發展的趨勢,大大加快咱們平時的開發和維護效率。

相關文章
相關標籤/搜索