AsyncDisplayKit的使用

AsyncDisplayKit的使用


一、簡介

AsyncDisplayKit 是一個UI框架,最初誕生于 Facebook 的 Paper 應用程序。它是為了解決 Paper 團隊面臨的核心問題之一:盡可能緩解主線程的壓力。它能在異步線程繪制修改UI,然后統一添加進內存渲染出來。

image

ASDK 的作者是 Scott Goodson (Linkedin),
他曾經在蘋果工作,負責 iOS 的一些內置應用的開發,比如股票、計算器、地圖、鐘表、設置、Safari 等,當然他也參與了 UIKit framework 的開發。后來他加入 Facebook 后,負責 Paper 的開發,創建并開源了 AsyncDisplayKit。目前他在 Pinterest 和 Instagram 負責 iOS 開發和用戶體驗的提升等工作。

1.解決的問題

很多時候用戶在操作app的時候,會感覺到不適那么流暢,有所卡頓。
ASDK主要就是解決的問題就是操作頁面過程中的保持幀率在60fps(理想狀態下)的問題。

造成卡頓的原因有很多,總結一句話基本上就是:
CPU或GPU消耗過大,導致在一次同步信號之間沒有準備完成,沒有內容提交,導致掉幀的問題。
具體的原理,在提升 iOS 界面的渲染性能文章中介紹的十分詳細了,這里也不多闡述了。

2.優化原理

  • 布局:
    iOS自帶的Autolayout在布局性能上存在瓶頸,并且只能在主線程進行計算。(參考Auto Layout Performance on iOS)因此ASDK棄用了Autolayout,自己參考自家的ComponentKit設計了一套布局方式。
  • 渲染
    對于大量文本,圖片等的渲染,UIKit組件只能在主線程并且可能會造成GPU繪制的資源緊張。ASDK使用了一些方法,比如圖層的預混合等,并且異步的在后臺繪制圖層,不阻塞主線程的運行。
  • 系統對象創建與銷毀
    UIKit組件封裝了CALayer圖層的對象,在創建、調整、銷毀的時候,都會在主線程消耗資源。ASDK自己設計了一套Node機制,也能夠調用。

實際上,從上面的一些解釋也可以看出,ASDK最大的特點就是"異步"。
將消耗時間的渲染、圖片解碼、布局以及其它 UI 操作等等全部移出主線程,這樣主線程就可以對用戶的操作及時做出反應,來達到流暢運行的目的。

ASDK 認為,阻塞主線程的任務,主要分為上面這三大類。

為了盡量優化性能,ASDK 嘗試對 UIKit 組件進行封裝:

3.Nodes節點

  • 如果你之前使用過views,那么你應該已經知道如何使用nodes,大部分的方法都有一個等效的node,大部分的UIView和CALayer的屬性都有類似的可用的。任何情況都會有一點點命名差異(例如,clipsToBounds和masksToBounds),node基本上都是默認使用UIView的名字,唯一的例外是node使用position而不是center

  • 當然,你也可以直接訪問底層view和layer,使用node.view和node.layer

  • 關系圖


    image

    這是常見的 UIView 和 CALayer 的關系:View 持有 Layer 用于顯示,View 響應觸摸事件。

關系圖

不需要響應觸摸事件
  • Node控件
ASDK UIKit
ASDisplayNode UIView
ASCellNode UITableViewCell/UICollectionViewCell
ASTextNode UILabel
ASImageNode/ASNetworkImageNode UIImageView
ASVideoNode AVPlayerLayer
ASControlNode UIControl
ASScrollNode UIScrollView
ASEditableTextNode UITextView
ASMultiplexImageNode(圖片管理) UIImageView

4.安裝

CocoaPods安裝

pod 'AsyncDisplayKit'

Carthage安裝

AsyncDisplayKit可以使用Carthage安裝,將下面的代碼添加進入Cartfile

github "facebook/AsyncDisplayKit"

在終端執行carthage update來構建AsyncDisplayKit庫,會自動在項目根目錄下生成Carthage名字的文件夾,里面有個build文件夾,可以用來framework到你打算使用的項目中

靜態庫

AsyncDisplayKit可以當做靜態庫引入

  • 拷貝整個工程到你的目錄下,添加AsyncDisplayKit.xcodeproj到你的workspace
  • 在build phases中,在Target Dependencies下添加AsyncDisplayKit Library
  • 在build phases中,添加libAsyncDisplayKit.a, AssetsLibrary, Photos等框架到Link Binary With Libraries中
  • 在build settings中,添加-lc++和-ObjC到 project linker flags

二、使用

主要介紹常用控件ASTableNode/ASCollectionNode的使用,代碼放在GitHub上的ASDK_Demo

1.ASImageNode

  • 使用ASNetworkImageNode的URL設置網絡圖片。
  • ASNetworkImageNode有圖片下載的ASNetworkImageNodeDelegate
  • ASImageNode使用ASDK的圖片管理類PINCache,PINRemoteImage
  • 如果不打算引入PINRemoteImage和PINCache,你會失去對jpeg的更好的支持,你需要自行引入你自己的cache系統,需要遵從ASImageCacheProtocol

2.ASTextNode

ASTextNode沒有text屬性,只能使用attributedText

//居中
NSMutableParagraphStyle * paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.alignment = NSTextAlignmentCenter;
        
labelNode.attributedText = [[NSAttributedString alloc] initWithString:@"居中文字" attributes:@{NSFontAttributeName: [UIFont systemFontOfSize:18],
                                                                                                      NSForegroundColorAttributeName:[UIColor blackColor],
                                                                                                      NSBackgroundColorAttributeName : [UIColor clearColor],
                                                                                                      NSParagraphStyleAttributeName:paragraphStyle}];

3.ASTableNode/ASCollectionNode

  • ASTableNode/ASCollectionNode不支持復用機制,每次滾動都會重新創建cell。
  • ASTableNode并不提供類似UITableview的-tableView:heightForRowAtIndexPath:方法,這是因為節點基于自己的約束來確定自己的高度,就是說你不再需要寫代碼來確定這個細節,一個node通過-layoutSpecThatFits:方法返回的布局規則確定了行高,所有的節點只要提供了約束大小,就有能力自己確定自己的尺寸
  • 使用 Batch Fetching 進行無限滾動,即預加載功能

三、布局

引用1
引用2

ASDK 擁有自己的一套成熟布局方案,雖然學習成本略高,但至少比原生的 AutoLayout 寫起來舒服,重點是性能比起 AutoLayout 好的不是一點點。(ASDK不支持autoLayout)

//下面這個方法就是用來建立布局規則對象,產生 node 大小以及所有子 node 大小的地方,你創建的布局規則對象一直持續到這個方法返回的時間點,經過了這個時間點后,它就不可變了。尤其重要要記住的一點事,千萬不要緩存布局規則對象,當你以后需要他的時候,請重新創建。
//調用時機:ASDisplayNode 在初始化之后會檢查是否有子視圖,如果有就會調用
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize 

1. 布局類

  • ASAbsoluteLayoutSpec(絕對布局約束)
  • ASBackgroundLayoutSpec(背景布局規則)
  • ASInsetLayoutSpec(邊距布局規則)
  • ASOverlayLayoutSpec(覆蓋布局規則)
  • ASRatioLayoutSpec(比例布局規則)
  • ASRelativeLayoutSpec(相對布局規則)
  • ASCenterLayoutSpec(中心布局規則)
  • ASStackLayoutSpec(堆疊布局規則)
  • ASWrapperLayoutSpec (填充布局規則)

2.示例

ASAbsoluteLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
  self.childNode.style.layoutPosition = CGPointMake(100, 100);
  self.childNode.style.preferredLayoutSize = ASLayoutSizeMake(ASDimensionMake(100), ASDimensionMake(100));

  ASAbsoluteLayoutSpec *absoluteLayout = [ASAbsoluteLayoutSpec absoluteLayoutSpecWithChildren:@[self.childNode]];
  return absoluteLayout;
}

使用方法和原生的絕對布局類似

ASBackgroundLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
  ASBackgroundLayoutSpec *backgroundLayout = [ASBackgroundLayoutSpec backgroundLayoutSpecWithChild:self.childNodeB background:self.childNodeA];
  return backgroundLayout;
}

把childNodeA 做為 childNodeB 的背景,也就是 childNodeB 在上層,要注意的是 ASBackgroundLayoutSpec 事實上根本不會改變視圖的層級關系

ASInsetLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
    ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_childNode];
    return insetLayout;
}

_childNode 相對于父視圖邊距都為 0,相當于填充整個父視圖。

ASOverlayLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{

  _photoNode.style.preferredSize = CGSizeMake(USER_IMAGE_HEIGHT*2, USER_IMAGE_HEIGHT*2);

  // INIFINITY(插入無邊界)
  UIEdgeInsets insets = UIEdgeInsetsMake(INFINITY, 12, 12, 12);
  ASInsetLayoutSpec *textInsetSpec = [ASInsetLayoutSpec insetLayoutSpecWithInsets:insets child:_titleNode];

  return [ASOverlayLayoutSpec overlayLayoutSpecWithChild:_photoNode 
                                                 overlay:textInsetSpec];

}

類似于ASBackgroundLayoutSpec,都是設置層級關系

ASRatioLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
    ASRatioLayoutSpec *ratioLayout = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0f child:self.childNodeA];
    return ratioLayout;
}

比較常用的一個類,作用是設置自身的高寬比,例如設置正方形的視圖

ASRelativeLayoutSpec

image
//把 childNodeA 顯示在右上角。
    self.childNodeA.style.preferredSize = CGSizeMake(100, 100);
    ASRelativeLayoutSpec *relativeLayout = [ASRelativeLayoutSpec relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionEnd verticalPosition:ASRelativeLayoutSpecPositionStart sizingOption:ASRelativeLayoutSpecSizingOptionDefault child:self.childNodeA];
    return relativeLayout;
}

它可以把視圖布局在:左上、左下、右上、右下四個頂點以外,還可以設置成居中布局。

ASCenterLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
    self.childNodeA.style.preferredSize = CGSizeMake(100, 100);
    ASCenterLayoutSpec *relativeLayout = [ASCenterLayoutSpec centerLayoutSpecWithCenteringOptions:ASCenterLayoutSpecCenteringXY sizingOptions:ASCenterLayoutSpecSizingOptionDefault child:self.childNodeA];
    return relativeLayout;
}

ASWrapperLayoutSpec

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{    
    ASWrapperLayoutSpec *wrapperLayout = [ASWrapperLayoutSpec wrapperWithLayoutElement:self.childNodeA];
    return wrapperLayout;
}

填充整個視圖

ASStackLayoutSpec

可以說這是最常用的類,而且相對于其他類來說在功能上是最接近于 AutoLayout 的。
之所以稱之為盒子布局是因為它和 CSS 中 Flexbox 很相似,不清楚 Flexbox 的可以看下這篇博客(HTML布局)

示例

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{

  // 當用戶名和位置信息文本太長時,收縮堆放視圖來適應屏幕,而不是將所有內容向右堆放
  ASStackLayoutSpec *nameLocationStack = [ASStackLayoutSpec verticalStackLayoutSpec];
  nameLocationStack.style.flexShrink = 1.0;
  nameLocationStack.style.flexGrow = 1.0;

  //如果從服務器獲取位置信息,并檢查位置信息是否可用
  if (_postLocationNode.attributedText) {
    nameLocationStack.children = @[_usernameNode, _postLocationNode];
  } else {
    nameLocationStack.children = @[_usernameNode];
  }

  //水平堆放
  ASStackLayoutSpec *headerStackSpec = [ASStackLayoutSpec   stackLayoutSpecWithDirection:ASStackLayoutDirectionHorizontal
                     spacing:40     
              justifyContent:ASStackLayoutJustifyContentStart
                  alignItems:ASStackLayoutAlignItemsCenter
                    children:@[nameLocationStack, _postTimeNode]];

  //插入堆放
  return [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsMake(0, 10, 0, 10)    
                                                child:headerStackSpec];

}

簡單的說明下各個參數的作用:
1.direction:主軸的方向,有兩個可選值:

  • 縱向:ASStackLayoutDirectionVertical(默認)
  • 橫向:ASStackLayoutDirectionHorizontal
    2.spacing: 主軸上視圖排列的間距,比如有四個視圖,那么它們之間的存在三個間距值都應該是spacing
    3.justifyContent: 主軸上的排列方式,有五個可選值:
  • ASStackLayoutJustifyContentStart 從前往后排列
  • ASStackLayoutJustifyContentCenter 居中排列
  • ASStackLayoutJustifyContentEnd 從后往前排列
  • ASStackLayoutJustifyContentSpaceBetween 間隔排列,兩端無間隔
  • ASStackLayoutJustifyContentSpaceAround 間隔排列,兩端有間隔
    4.alignItems: 交叉軸上的排列方式,有五個可選值:
  • ASStackLayoutAlignItemsStart 從前往后排列
  • ASStackLayoutAlignItemsEnd 從后往前排列
  • ASStackLayoutAlignItemsCenter 居中排列
  • ASStackLayoutAlignItemsStretch 拉伸排列
  • ASStackLayoutAlignItemsBaselineFirst 以第一個文字元素基線排列(主軸是橫向才可用)
  • ASStackLayoutAlignItemsBaselineLast 以最后一個文字元素基線排列(主軸是橫向才可用)
    5.children: 包含的視圖。數組內元素順序同樣代表著布局時排列的順序

四、優缺點

  • 不支持大家常用的storyboard、xib、autoLayout,影響開發效率
  • 代碼沒有UIKit使用熟練
  • 網上資源少
  • 但是可以和UIKit混合開發
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評論 6 535
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,740評論 3 420
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,856評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,175評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,931評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,321評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,533評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,082評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,891評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,067評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,319評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,732評論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,987評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,794評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,076評論 2 375

推薦閱讀更多精彩內容