AsyncDisplayKit的使用
一、簡介
AsyncDisplayKit 是一個UI框架同欠,最初誕生于 Facebook 的 Paper 應(yīng)用程序镀首。它是為了解決 Paper 團(tuán)隊面臨的核心問題之一:盡可能緩解主線程的壓力。它能在異步線程繪制修改UI碳蛋,然后統(tǒng)一添加進(jìn)內(nèi)存渲染出來钧忽。
ASDK 的作者是 Scott Goodson (Linkedin)毯炮,
他曾經(jīng)在蘋果工作,負(fù)責(zé) iOS 的一些內(nèi)置應(yīng)用的開發(fā)耸黑,比如股票桃煎、計算器、地圖大刊、鐘表为迈、設(shè)置三椿、Safari 等,當(dāng)然他也參與了 UIKit framework 的開發(fā)葫辐。后來他加入 Facebook 后搜锰,負(fù)責(zé) Paper 的開發(fā),創(chuàng)建并開源了 AsyncDisplayKit耿战。目前他在 Pinterest 和 Instagram 負(fù)責(zé) iOS 開發(fā)和用戶體驗的提升等工作蛋叼。
1.解決的問題
很多時候用戶在操作app的時候,會感覺到不適那么流暢剂陡,有所卡頓狈涮。
ASDK主要就是解決的問題就是操作頁面過程中的保持幀率在60fps(理想狀態(tài)下)的問題。
造成卡頓的原因有很多鸭栖,總結(jié)一句話基本上就是:
CPU或GPU消耗過大歌馍,導(dǎo)致在一次同步信號之間沒有準(zhǔn)備完成,沒有內(nèi)容提交晕鹊,導(dǎo)致掉幀的問題松却。
具體的原理,在提升 iOS 界面的渲染性能文章中介紹的十分詳細(xì)了溅话,這里也不多闡述了晓锻。
2.優(yōu)化原理
- 布局:
iOS自帶的Autolayout在布局性能上存在瓶頸,并且只能在主線程進(jìn)行計算公荧。(參考Auto Layout Performance on iOS)因此ASDK棄用了Autolayout带射,自己參考自家的ComponentKit設(shè)計了一套布局方式。 - 渲染
對于大量文本循狰,圖片等的渲染窟社,UIKit組件只能在主線程并且可能會造成GPU繪制的資源緊張。ASDK使用了一些方法绪钥,比如圖層的預(yù)混合等灿里,并且異步的在后臺繪制圖層,不阻塞主線程的運行程腹。 - 系統(tǒng)對象創(chuàng)建與銷毀
UIKit組件封裝了CALayer圖層的對象匣吊,在創(chuàng)建、調(diào)整寸潦、銷毀的時候色鸳,都會在主線程消耗資源。ASDK自己設(shè)計了一套Node機(jī)制见转,也能夠調(diào)用命雀。
實際上,從上面的一些解釋也可以看出斩箫,ASDK最大的特點就是"異步"吏砂。
將消耗時間的渲染撵儿、圖片解碼、布局以及其它 UI 操作等等全部移出主線程狐血,這樣主線程就可以對用戶的操作及時做出反應(yīng)淀歇,來達(dá)到流暢運行的目的。
ASDK 認(rèn)為匈织,阻塞主線程的任務(wù)浪默,主要分為上面這三大類。
為了盡量優(yōu)化性能报亩,ASDK 嘗試對 UIKit 組件進(jìn)行封裝:
3.Nodes節(jié)點
如果你之前使用過views浴鸿,那么你應(yīng)該已經(jīng)知道如何使用nodes井氢,大部分的方法都有一個等效的node弦追,大部分的UIView和CALayer的屬性都有類似的可用的。任何情況都會有一點點命名差異(例如花竞,clipsToBounds和masksToBounds)劲件,node基本上都是默認(rèn)使用UIView的名字,唯一的例外是node使用position而不是center
當(dāng)然约急,你也可以直接訪問底層view和layer零远,使用node.view和node.layer
-
關(guān)系圖
image
這是常見的 UIView 和 CALayer 的關(guān)系:View 持有 Layer 用于顯示,View 響應(yīng)觸摸事件厌蔽。
- 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安裝牵辣,將下面的代碼添加進(jìn)入Cartfile
github "facebook/AsyncDisplayKit"
在終端執(zhí)行carthage update來構(gòu)建AsyncDisplayKit庫,會自動在項目根目錄下生成Carthage名字的文件夾奴饮,里面有個build文件夾纬向,可以用來framework到你打算使用的項目中
靜態(tài)庫
AsyncDisplayKit可以當(dāng)做靜態(tài)庫引入
- 拷貝整個工程到你的目錄下,添加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設(shè)置網(wǎng)絡(luò)圖片江锨。
- ASNetworkImageNode有圖片下載的ASNetworkImageNodeDelegate
- ASImageNode使用ASDK的圖片管理類PINCache,PINRemoteImage
- 如果不打算引入PINRemoteImage和PINCache吃警,你會失去對jpeg的更好的支持,你需要自行引入你自己的cache系統(tǒng)啄育,需要遵從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不支持復(fù)用機(jī)制,每次滾動都會重新創(chuàng)建cell灸撰。
- ASTableNode并不提供類似UITableview的-tableView:heightForRowAtIndexPath:方法谒府,這是因為節(jié)點基于自己的約束來確定自己的高度拼坎,就是說你不再需要寫代碼來確定這個細(xì)節(jié),一個node通過-layoutSpecThatFits:方法返回的布局規(guī)則確定了行高完疫,所有的節(jié)點只要提供了約束大小泰鸡,就有能力自己確定自己的尺寸
- 使用 Batch Fetching 進(jìn)行無限滾動,即預(yù)加載功能
三壳鹤、布局
ASDK 擁有自己的一套成熟布局方案盛龄,雖然學(xué)習(xí)成本略高,但至少比原生的 AutoLayout 寫起來舒服芳誓,重點是性能比起 AutoLayout 好的不是一點點余舶。(ASDK不支持autoLayout)
//下面這個方法就是用來建立布局規(guī)則對象,產(chǎn)生 node 大小以及所有子 node 大小的地方锹淌,你創(chuàng)建的布局規(guī)則對象一直持續(xù)到這個方法返回的時間點匿值,經(jīng)過了這個時間點后,它就不可變了赂摆。尤其重要要記住的一點事挟憔,千萬不要緩存布局規(guī)則對象,當(dāng)你以后需要他的時候烟号,請重新創(chuàng)建绊谭。
//調(diào)用時機(jī):ASDisplayNode 在初始化之后會檢查是否有子視圖,如果有就會調(diào)用
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
1. 布局類
- ASAbsoluteLayoutSpec(絕對布局約束)
- ASBackgroundLayoutSpec(背景布局規(guī)則)
- ASInsetLayoutSpec(邊距布局規(guī)則)
- ASOverlayLayoutSpec(覆蓋布局規(guī)則)
- ASRatioLayoutSpec(比例布局規(guī)則)
- ASRelativeLayoutSpec(相對布局規(guī)則)
- ASCenterLayoutSpec(中心布局規(guī)則)
- ASStackLayoutSpec(堆疊布局規(guī)則)
- ASWrapperLayoutSpec (填充布局規(guī)則)
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 事實上根本不會改變視圖的層級關(guān)系
ASInsetLayoutSpec
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
ASInsetLayoutSpec *inset = [ASInsetLayoutSpec insetLayoutSpecWithInsets:UIEdgeInsetsZero child:_childNode];
return insetLayout;
}
_childNode 相對于父視圖邊距都為 0,相當(dāng)于填充整個父視圖迫筑。
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宪赶,都是設(shè)置層級關(guān)系
ASRatioLayoutSpec
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize{
ASRatioLayoutSpec *ratioLayout = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:1.0f child:self.childNodeA];
return ratioLayout;
}
比較常用的一個類,作用是設(shè)置自身的高寬比铣焊,例如設(shè)置正方形的視圖
ASRelativeLayoutSpec
//把 childNodeA 顯示在右上角逊朽。
self.childNodeA.style.preferredSize = CGSizeMake(100, 100);
ASRelativeLayoutSpec *relativeLayout = [ASRelativeLayoutSpec relativePositionLayoutSpecWithHorizontalPosition:ASRelativeLayoutSpecPositionEnd verticalPosition:ASRelativeLayoutSpecPositionStart sizingOption:ASRelativeLayoutSpecSizingOptionDefault child:self.childNodeA];
return relativeLayout;
}
它可以把視圖布局在:左上、左下曲伊、右上叽讳、右下四個頂點以外,還可以設(shè)置成居中布局坟募。
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{
// 當(dāng)用戶名和位置信息文本太長時,收縮堆放視圖來適應(yīng)屏幕,而不是將所有內(nèi)容向右堆放
ASStackLayoutSpec *nameLocationStack = [ASStackLayoutSpec verticalStackLayoutSpec];
nameLocationStack.style.flexShrink = 1.0;
nameLocationStack.style.flexGrow = 1.0;
//如果從服務(wù)器獲取位置信息,并檢查位置信息是否可用
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];
}
簡單的說明下各個參數(shù)的作用:
1.direction
:主軸的方向,有兩個可選值:
- 縱向:
ASStackLayoutDirectionVertical
(默認(rèn))- 橫向:
ASStackLayoutDirectionHorizontal
2.spacing
: 主軸上視圖排列的間距赚哗,比如有四個視圖她紫,那么它們之間的存在三個間距值都應(yīng)該是spacing
3.justifyContent
: 主軸上的排列方式硅堆,有五個可選值:ASStackLayoutJustifyContentStart
從前往后排列ASStackLayoutJustifyContentCenter
居中排列ASStackLayoutJustifyContentEnd
從后往前排列ASStackLayoutJustifyContentSpaceBetween
間隔排列,兩端無間隔ASStackLayoutJustifyContentSpaceAround
間隔排列贿讹,兩端有間隔
4.alignItems
: 交叉軸上的排列方式渐逃,有五個可選值:ASStackLayoutAlignItemsStart
從前往后排列ASStackLayoutAlignItemsEnd
從后往前排列ASStackLayoutAlignItemsCenter
居中排列ASStackLayoutAlignItemsStretch
拉伸排列ASStackLayoutAlignItemsBaselineFirst
以第一個文字元素基線排列(主軸是橫向才可用)ASStackLayoutAlignItemsBaselineLast
以最后一個文字元素基線排列(主軸是橫向才可用)
5.children
: 包含的視圖。數(shù)組內(nèi)元素順序同樣代表著布局時排列的順序
四民褂、優(yōu)缺點
- 不支持大家常用的storyboard茄菊、xib、autoLayout赊堪,影響開發(fā)效率
- 代碼沒有UIKit使用熟練
- 網(wǎng)上資源少
- 但是可以和UIKit混合開發(fā)