AsyncDisplaykit(Texture)之布局篇

前言

Texture的基本使用單元是node. ASDisplayNode是一個UIView層之上的封裝铅匹,就像UIView是對CALayer的封裝一樣押赊。跟View不一樣的是,node是線程安全(比如uiview的操作就不是線程安全的包斑,在非UI線程無法操作UIView)的流礁,就是說你在非主線程對node進(jìn)行初始化以及配置它們的層級操作都是安全的。

今天的主題是布局罗丰,Texture擁有自己的一套成熟布局方案神帅,雖然學(xué)習(xí)成本略高,但至少比蓋原萌抵。而且AutoLayout寫起來舒服找御,重點是性能遠(yuǎn)好于AutoLayout元镀,Texture文檔上也。指出了這套布局方案的的優(yōu)點:

  • 快速:與手動布局代碼一樣快霎桅,比自動布局更快
  • 異步和并發(fā):可以在后臺線程上計算布局栖疑,以便不中斷用戶交互。
  • 聲明性:使用不可變數(shù)據(jù)結(jié)構(gòu)顯示布局滔驶。這使得布局代碼更易于開發(fā)遇革,文檔,代碼審查揭糕,測試澳淑,調(diào)試,配置文件和維護(hù)插佛。
  • 可緩存:布局結(jié)果是不可變數(shù)據(jù)結(jié)構(gòu)杠巡,因為它們可以在后臺預(yù)先計算并緩存以提高用戶感知性能。
  • 可擴(kuò)展:易于在類之間共享代碼雇寇。

章節(jié)

一氢拥、快速了解
基礎(chǔ)元素 含義 基本概念
Layout Specs 布局規(guī)則 LayoutSpecs包含并排列LayoutElements。這意味著您可以從Nodes和其他LayoutSpecs構(gòu)成LayoutSpecs
Layout Elements 布局元素 ASDisplayNodes和ASLayoutSpecs都符合<ASLayoutElement>協(xié)議

ASLayoutElement Properties(布局元素屬性)

屬性 類型 描述
.style.width ASDimension 設(shè)置元素的寬度锨侯。 會被minWidth和maxWidth覆蓋嫩海。默認(rèn)為ASDimensionAuto
.style.height ASDimension 設(shè)置元素的高度。 會被minHeight和maxHeight覆蓋囚痴。默認(rèn)為ASDimensionAuto叁怪。
.style.minHeight ASDimension 設(shè)置元素的最大高度。 它防止height屬性的已使用值變得大于為maxHeight指定的值深滚。 maxHeight的值覆蓋height奕谭,但minHeight覆蓋maxHeight。默認(rèn)為ASDimensionAuto
.style.maxHeight ASDimension 如果子元素的堆棧大小的總和大于最大大小痴荐,那么這個對象是否應(yīng)該縮小呢血柳?
.style.minWidth ASDimension 設(shè)置元素的最小寬度。它防止width屬性的使用值變得小于為minWidth指定的值生兆。 minWidth的值覆蓋maxWidth和width难捌。默認(rèn)為ASDimensionAuto
.style.maxWidth ASDimension 設(shè)置元素的最大寬度。 它防止width屬性的使用值變得大于為maxWidth指定的值鸦难。 maxWidth的值覆蓋width根吁,但minWidth覆蓋maxWidth。默認(rèn)為ASDimensionAuto
.style.preferredSize **CGSize ** 提供布局元素的建議大小合蔽。 如果提供了可選的minSize或maxSize击敌,且preferredSize超過這些,則將強(qiáng)制執(zhí)行minSize或maxSize, 如果未提供此可選值辈末,則布局元素的大小將默認(rèn)為其提供的內(nèi)在內(nèi)容大小calculateSizeThatFits:
.style.minSize **CGSize ** ~
.style.maxSize **CGSize ** ~
.style.preferredLayoutSize ASLayoutSize ~
.style.minLayoutSize ASLayoutSize ~
.style.maxLayoutSize ASLayoutSize ~

1愚争、一些Node需要固定大小

一些元素具有一個”固有大小“映皆,基于他們可用內(nèi)容。
ASTextNode可以根據(jù)其屬性字符串計算其大小轰枝,其他具有固有大小的Node包括:
ASImageNode
ASButtonNode
在從URL下載圖像之前捅彻,ASNetworkImageNode不知道它的大小。這些種類包括:
ASVideoNode
ASVideoPlayerNode
ASNetworkImageNode
注意:
缺少初始固有大小的這些Node必須設(shè)置它們的初始大小鞍陨,使用ASRatioLayoutSpec(“比例布局規(guī)則”)步淹,ASAbsoluteLayoutSpec(“絕對布局規(guī)則”)或者對象的size屬性。

2诚撵、Layout調(diào)試

在任何ASDisplayNode或ASLayoutSpec上調(diào)用-asciiArtString缭裆,會返回對象及其子對象的字符圖。
(可選)如果在任何Node或layoutSpec上設(shè)置.debugName寿烟,那么也將包含在字符圖澈驼。
例如:
  -----------------------ASStackLayoutSpec----------------------
|  -----ASStackLayoutSpec-----  -----ASStackLayoutSpec-----  |
|  |       ASImageNode       |  |       ASImageNode       |  |
|  |       ASImageNode       |  |       ASImageNode       |  |
|  ---------------------------  ---------------------------  |
--------------------------------------------------------------
#可以在任何ASLayoutElement(node或layoutSpec)上打印對象樣式,調(diào)整大小屬性時極其方便筛武。
例如:
(lldb) po _photoImageNode.style
Layout Size = min {414pt, 414pt} <= preferred {20%, 50%} <= max {414pt, 414pt}

3缝其、需要使用原生控件

let node = ASDisplayNode { () -> UIView in
    let view = SomeView()
    return view
}
二、Layout Specs(布局規(guī)則)

ASDisplayNode 在初始化之后會檢查是否有子視圖徘六,如果有就會調(diào)用以下方法進(jìn)行布局内边,所以對視圖進(jìn)行布局需要重寫這個方法(該方法返回的必須是 ASLayoutSpec)

func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec 
以下ASLayoutSpec的常用布局子類,用于組成簡單或復(fù)雜的布局
布局規(guī)則 說明
ASInsetLayoutSpec 插入布局
ASOverlayLayoutSpec 覆蓋布局
ASBackgroundLayoutSpec 背景布局
ASCenterLayoutSpec 中心布局
ASRatioLayoutSpec 比例布局
ASStackLayoutSpec 堆疊布局
ASAbsoluteLayoutSpec 絕對布局

1. ASInsetLayoutSpec(插入布局)

在布局過程中待锈,ASInsetLayoutSpec通過constrainedSize.max傳遞插入減掉后的CGSize給子項漠其,
一旦子項確定它的最終尺寸,插入規(guī)則將其最終尺寸加上其插入邊距向上傳遞竿音,
由于插圖布局規(guī)則的大小基于其子項的大小和屎,所以子項必須具有固有大小或明確設(shè)置其大小。

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
  let insets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0)
  let headerWithInset = ASInsetLayoutSpec(insets: insets, child: textNode)
  return  headerWithInset
}

ASInsetLayoutSpec.png

2. ASOverlayLayoutSpec(覆蓋布局)


ASOverlayLayoutSpec布局一個組件(foregroundNode)谍失,作為覆蓋伸展到另個組件(backgroundNode)之前覆蓋布局的大小眶俩,
是根據(jù)子項的大小計算得出的。下圖中快鱼,子項是backgroundNode,然后子項的大小作為constrainedSize傳遞給覆蓋布局元素(foregroundNode)纲岭,子項(backgroundNode)必須具有固有大小或在其上設(shè)置的大小抹竹。

override func layoutSpecThatFits(_ constrainedSize:ASSizeRange) - > ASLayoutSpec
{
  let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)
  let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
  return ASOverlayLayoutSpec(child:backgroundNode,overlay:foregroundNode)
}

3. ASBackgroundLayoutSpec(背景布局)


ASBackgroundLayoutSpec布局一個組件(backgroundNode)止潮,作為背景伸展到另一個組件(foregroundNode)之后背景布局的大小窃判,是根據(jù)子項的大小計算得出的。下圖中喇闸,子項是藍(lán)色層袄琳,然后询件,子項的大小作為constrainedSize傳遞給背景布局元素(backgroundNode),子項(foregroundNode)必須具有固有大小或在其上設(shè)置的大小唆樊。

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
  let backgroundNode = ASDisplayNodeWithBackgroundColor(UIColor.red)
  let foregroundNode = ASDisplayNodeWithBackgroundColor(UIColor.blue)

  return ASBackgroundLayoutSpec(child: foregroundNode, background: backgroundNode)
}

4. ASCenterLayoutSpec(中心布局)


ASCenterLayoutSpec將其子項居中在其最大值中constrainedSize宛琅。
如果中心規(guī)格的寬度或高度不受約束,它會縮小到子項的大小逗旁。

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
  let subnode = ASDisplayNodeWithBackgroundColor(UIColor.green, CGSize(width: 60.0, height: 100.0))
  let centerSpec = ASCenterLayoutSpec(centeringOptions: .XY, sizingOptions: [], child: subnode)
  return centerSpec
}

ASCenterLayoutSpec的兩個屬性:

屬性 說明
centeringOptions 確定中心位置 None嘿辟,X,Y片效,XY
sizingOptions 確定中心占用空間 Default红伦,minimum X,minimum Y淀衣,minimum XY

5. ASRatioLayoutSpec(比例布局)


ASRatioLayoutSpec布局縮放固定寬高比昙读,此規(guī)則必須具有作為constrainedSize傳遞給它的寬度或高度,因為它使用此值來縮放自身膨桥。
使用比例布局為ASNetworkImageNode或ASVideoNode提供固有大小是非常常見的蛮浑,因為兩者在服務(wù)器返回內(nèi)容之前都沒有內(nèi)在大小。

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
  // 一半比例
  let subnode = ASDisplayNodeWithBackgroundColor(UIColor.green, CGSize(width: 100, height: 100.0))
  let ratioSpec = ASRatioLayoutSpec(ratio: 0.5, child: subnode)
  return ratioSpec
}

6. ASAbsoluteLayoutSpec(絕對布局)


在ASAbsoluteLayoutSpec中国撵,可以通過設(shè)置其layoutPosition屬性來指定其子元素的確切位置(x / y坐標(biāo))陵吸,絕對布局比其他類型的布局更不靈活和難以維護(hù)。

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec
{
  let maxConstrainedSize = constrainedSize.max

  guitarVideoNode.style.layoutPosition = CGPoint.zero
  guitarVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width, height: maxConstrainedSize.height / 3.0)

  nicCageVideoNode.style.layoutPosition = CGPoint(x: maxConstrainedSize.width / 2.0, y: maxConstrainedSize.height / 3.0)
  nicCageVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)

  simonVideoNode.style.layoutPosition = CGPoint(x: 0.0, y: maxConstrainedSize.height - (maxConstrainedSize.height / 3.0))
  simonVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)

  hlsVideoNode.style.layoutPosition = CGPoint(x: 0.0, y: maxConstrainedSize.height / 3.0)
  hlsVideoNode.style.preferredSize = CGSize(width: maxConstrainedSize.width / 2.0, height: maxConstrainedSize.height / 3.0)

  return ASAbsoluteLayoutSpec(children: [guitarVideoNode, nicCageVideoNode, simonVideoNode, hlsVideoNode])
}

ASAbsoluteLayoutSpec屬性:

屬性 說明
sizing 大小 Default / Size to Fit

確定絕對規(guī)格將占用多少空間介牙。

7. ASStackLayoutSpec(堆疊布局)


在ASDK中的所有l(wèi)ayoutSpecs中壮虫,ASStackLayoutSpec是非常強(qiáng)大的,ASStackLayoutSpec使用flexbox算法來確定其子節(jié)點的位置和大小环础,F(xiàn)lexbox旨在在不同的屏幕尺寸上提供一致的布局囚似,在堆疊布局中,以垂直或水平堆疊對齊item线得。堆疊布局可以是另一個堆疊布局的子布局饶唤,這使得可以使用堆疊布局規(guī)則創(chuàng)建幾乎任何布局。

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec 
{
  let mainStack = ASStackLayoutSpec(direction: .horizontal,
                                    spacing: 6.0,
                                    justifyContent: .start,
                                    alignItems: .center,
                                    children: [titleNode, subtitleNode])

  mainStack.style.minWidth = ASDimensionMakeWithPoints(60.0)
  mainStack.style.maxHeight = ASDimensionMakeWithPoints(40.0)

  return mainStack
}

ASStackLayoutSpec除了ASLayoutElement還有7個屬性:

屬性 說明 描述
direction 方向 指定堆疊方向,如果設(shè)置了horizontalAlignment和verticalAlignment,它們將被再次解決贯钩,導(dǎo)致justifyContent和alignItems被相應(yīng)地更新募狂。
spacing 間距 每個子元素之間的距離。
horizontalAlignment 水平對齊 指定子元素如何水平排列,取決于堆疊方向角雷,設(shè)置對齊會導(dǎo)致justifyContent或alignItems被更新祸穷。未來方向更改后,對齊將保持有效勺三。因此雷滚,優(yōu)選那些性質(zhì)。
verticalAlignment 豎直對齊 指定子元素如何垂直排列,取決于堆疊方向吗坚,設(shè)置對齊會導(dǎo)致justifyContent或alignItems被更新祈远。未來方向更改后呆万,對齊將保持有效。因此车份,優(yōu)選那些性質(zhì)谋减。
justifyContent 對齊內(nèi)容 每個子元素之間的距離。
alignItems 對齊Item 子元素沿著橫軸的方向躬充。
baselineRelativeArrangement 基線相對布局 如果YES逃顶,則從頂視圖的最后基線到底視圖的頂部測量兩個視圖之間的垂直間距。

ASStackLayoutElement Properties(堆疊布局元素屬性)

屬性 類型 描述
.style.spacingBefore **CGFloat ** 在堆疊方向上放置此對象之前的額外空間充甚。
.style.spacingAfter **CGFloat ** 在堆疊方向上放置此對象之后的額外空間以政。
.style.flexGrow **CGFloat ** 如果子元素的堆疊大小的總和小于最小大小,那么這個對象是否增長伴找?
.style.flexShrink **CGFloat ** 如果子元素的堆疊大小的總和大于最大大小盈蛮,那么這個對象是否縮小技矮?
.style.flexBasis ASDimension 使用flexGrow或flexShrink屬性并分配剩余空間之前抖誉,在堆棧維度(水平或垂直)中指定此對象的初始大小。
.style.alignSelf **ASStackLayoutAlignSelf ** 沿著橫軸的對象的方向衰倦,覆蓋alignItems袒炉。(ASStackLayoutAlignSelfAuto,ASStackLayoutAlignSelfStart樊零, ASStackLayoutAlignSelfEnd我磁, ASStackLayoutAlignSelfCenter, ASStackLayoutAlignSelfStretch)
.style.ascender **CGFloat ** 用于基線對準(zhǔn)驻襟。從對象的頂部到其基線的距離夺艰。
.style.descender **CGFloat ** 用于基線對準(zhǔn)。從對象的底部部到其基線的距離
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剪撬,更是在濱河造成了極大的恐慌,老刑警劉巖筏餐,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)愕贡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來巷屿,“玉大人,你說我怎么就攤上這事墩虹≈鼋恚” “怎么了憨琳?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長旬昭。 經(jīng)常有香客問我篙螟,道長,這世上最難降的妖魔是什么问拘? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任遍略,我火速辦了婚禮,結(jié)果婚禮上骤坐,老公的妹妹穿的比我還像新娘绪杏。我一直安慰自己,他們只是感情好纽绍,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布蕾久。 她就那樣靜靜地躺著,像睡著了一般拌夏。 火紅的嫁衣襯著肌膚如雪僧著。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天障簿,我揣著相機(jī)與錄音盹愚,去河邊找鬼。 笑死站故,一個胖子當(dāng)著我的面吹牛皆怕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播世蔗,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼端逼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了污淋?” 一聲冷哼從身側(cè)響起顶滩,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寸爆,沒想到半個月后礁鲁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡赁豆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年仅醇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片魔种。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡析二,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情叶摄,我是刑警寧澤属韧,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站蛤吓,受9級特大地震影響宵喂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜会傲,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一锅棕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧淌山,春花似錦裸燎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至王浴,卻和暖如春脆炎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氓辣。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工秒裕, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人钞啸。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓几蜻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親体斩。 傳聞我的和親對象是個殘疾皇子梭稚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345