了解 AsyncDisplayKit
AsyncDisplayKit的基本單位是節(jié)點 Node。一個Asdisplaynode 是 UIView 的抽象,反過來是CALayer抽象谓厘。與只能在主線程上使用的 UIView 視圖不同叮叹,節(jié)點是線程安全的:您可以在后臺線程上并行地實例化和配置它們的整個層次結(jié)構(gòu)。
為了保持它的用戶界面流暢和響應(yīng)派阱,你的應(yīng)用程序應(yīng)該以每秒60幀的速度呈現(xiàn)切诀。這意味著主線程有六十分之一秒把每幀揩环。這是16毫秒執(zhí)行所有的布局和繪圖代碼!而且由于系統(tǒng)開銷幅虑,您的代碼通常不到十毫秒才能運行它導(dǎo)致幀下降丰滑。
AsyncDisplayKit 讓你 Image 解碼、文本大小和渲染倒庵,布局吨枉,和其他昂貴的UI操作關(guān)閉主線程,讓主線程可以響應(yīng)用戶交互哄芜。
這段話翻譯于 AsyncDisplayKit GitHub 的介紹, 我們可以 通過 CocoaPods
or Carthage
安裝使用; 另外 目前 AsyncDisplayKit 已經(jīng)改名為 Texture。
關(guān)于 AsyncDisplayKit 的更多深層的理解, 本人參考了以下 文章
- AsyncDisplayKit介紹之一: 原理和思路
- AsyncDisplayKit系列之二:布局系統(tǒng)
- AsyncDisplayKit系列之三:深度優(yōu)化列表性能
- AsyncDisplaykit2.0使用「復(fù)雜界面流暢性」
- AsyncDisplayKit 系列教程 —— 集成柬唯、示例
- AsyncDisplayKit近一年的使用體會及疑難點
使用
為了更加深入理解 AsyncDisplayKit 這個框架, 我嘗試 進行 AsyncDisplayKit的簡單使用
基于
AsyncDisplayKit 2.2.1
Swift 4.0
ASTextNode
ASTextNode 相當于 UILabel , 不同的是 它不能設(shè)置 text 只能設(shè)置 attributedText
// MARK:- Use ASTextNode
func buildTextNode(textContent: String) {
let textLabel = ASTextNode()
if !textContent.isEmpty {
textLabel.attributedText = NSAttributedString(string: textContent, attributes:
[NSAttributedStringKey.foregroundColor : UIColor.white,
NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16),
NSAttributedStringKey.backgroundColor : UIColor.black]
)
}
let margin:CGFloat = 15
let width:CGFloat = XW_SCREEN_WIDTH - margin * 2
textLabel.layoutThatFits(ASSizeRange.init(min: CGSize.init(width: width, height: 30), max: CGSize.init(width: width, height: 300)))
textLabel.frame = CGRect.init(x: margin, y: 90, width: textLabel.calculatedSize.width, height: textLabel.calculatedSize.height)
self.view.addSubnode(textLabel)
}
我們 可以使用 layoutThatFits 來自行計算, 自適應(yīng) label的內(nèi)容 , ASSizeRange 來限定 最小 和最大的 size
然后, 設(shè)置frame 我們可以直接獲取到 calculatedSize , 是不是 還是蠻方便的呢 ?
最后 使用 .addSubnode 代替 .addSubView
ASImageNode
// MARK:- Use ASImageNode
func buildImageNode(imageURLString: String) {
let imageView = ASNetworkImageNode()
imageView.frame = CGRect.init(x: 80, y: 400, width: 200, height: 200)
imageView.backgroundColor = UIColor.green
imageView.contentMode = .scaleAspectFill
if !imageURLString.isEmpty {
imageView.url = URL.init(string: imageURLString)
}
self.view.addSubnode(imageView)
}
如果 你使用本地 的image 可能你只需要 使用 ASImageNode
但是加載 網(wǎng)絡(luò)圖片 就需要 使用到 ASNetworkImageNode
ASNetworkImageNode 默認用的緩存機制和圖片下載器是 PinRemoteImage认臊,為了使用我們自己的緩存機制和圖片下載器,需要實現(xiàn) ASImageCacheProtocol 圖片緩存協(xié)議和 ASImageDownloaderProtocol 圖片下載器協(xié)議兩個協(xié)議
ASButtonNode
// MARK:- Use ASButtonNode
func buildButtonNode(buttonName: String) {
let buttonNode = ASButtonNode()
if !buttonName.isEmpty {
buttonNode.setTitle(buttonName, with: UIFont.systemFont(ofSize: 14), with: UIColor.black, for: UIControlState.normal)
}
buttonNode.backgroundColor = UIColor.yellow
buttonNode.frame = CGRect.init(x: 80, y: 640, width: 200, height: 30)
buttonNode.addTarget(self, action: #selector(clickedButton(sender:)), forControlEvents: .touchUpInside)
self.view.addSubnode(buttonNode)
}
需要 注意 ASButtonNode 繼承于 ASControlNode 可以設(shè)置 add target, 有屬性
ASTextNode * titleNode;
ASImageNode * imageNode;
ASImageNode * backgroundImageNode;
ASControlNode
ASImageNode锄奢、ASButtonNode失晴、ASTextNode 同為 ASControlNode 子類,可以直接使用 .addTarget(self, action: "handleXXX", forControlEvents: .TouchUpInside) 為它們添加點擊響應(yīng)事件拘央,而避免使用addGesture等方法涂屁。
ASTableNode
我們可以使用 ASTableNode 來 著力解決 UITableView 在 ReloadData 耗時長以及滑動卡頓的性能問題
初始化 let tableView = ASTableNode.init(style: UITableViewStyle.plain)
代理 ASTableViewDataSource, ASTableViewDelegate
ASTableDataSource
//MARK:- ASTableDataSource
func numberOfSections(in tableNode: ASTableNode) -> Int {
return 1
}
func tableNode(_ tableNode: ASTableNode, numberOfRowsInSection section: Int) -> Int {
return mockData.count
}
func tableNode(_ tableNode: ASTableNode, nodeForRowAt indexPath: IndexPath) -> ASCellNode {
let cellNode = CustomCellNode()
return cellNode
}
.....
ASTableDataSource
//MARK:- ASTableDelegate
func tableNode(_ tableNode: ASTableNode, didSelectRowAt indexPath: IndexPath) {
}
func tableNode(_ tableNode: ASTableNode, willDisplayRowWith node: ASCellNode) {
}
.....
需要注意 ASTableNode 的高度計算以及布局都在 ASCellNode 中實現(xiàn),與 ASTableNode 是完全解耦的灰伟。
ASTableNode 中所有的元素都不支持 AutoLayout拆又、AutoResizing儒旬,也不支持StoryBoard、IB帖族。
ASTableNode 完全可以將滑動性能提升至60FPS栈源。
ASTableNode 實質(zhì)上是一個 ScrollView ,其中添加有指定數(shù)的 ASDisplayNode竖般,在屏幕滾動時甚垦,離屏的ASDisplayNode內(nèi)容會被暫時釋放,在屏或接近在屏的ASDisplayNode會被提前加載涣雕。因此艰亮,ASTableView 不存在 Cell 復(fù)用的問題,也不存在任何 Cell 復(fù)用挣郭。
ASCellNode
通常我們 自定義 UITableViewCell 是繼承于 ASCellNode
執(zhí)行以下步驟
-
override init()
創(chuàng)建 對應(yīng) 的 node, 添加進去 - 添加數(shù)據(jù) function
-
override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize
計算控件寬度和高度迄埃,并返回 Cell 的高度, 我們一般 需要計算的 東西 都是在這個方法里面進行layoutThatFits
-
override func layout()
各個控件 進行布局 , 通常 調(diào)用calculatedSize
進行 自適應(yīng)布局
ASDisplayNode
對于一些 不支持 的控件, 列如 UISlider, UISwitch, UIActivityIndicatorView, 并沒有對應(yīng)的 ASDisplayNode 子類實現(xiàn)。因此丈屹,我們需要創(chuàng)建一個 ASDisplayNode 调俘,使用block方法返回;
這里以一個 UIActivityIndicatorView
為例
let activityIndicator = ASDisplayNode.init(viewBlock: { () -> UIView in
let view = UIActivityIndicatorView(activityIndicatorStyle: .gray)
view.backgroundColor = UIColor.clear
view.hidesWhenStopped = true
return view
})
使用同樣的方法,可以添加任意類型 UIView 到 CellNode 中旺垒,這樣就不需要被 AsyncDisplayKit 束縛我們的應(yīng)用了彩库。
使用感想
首先, 本文只是對 AsyncDisplayKit 的一些基礎(chǔ) 控件 進行初窺使用; 通過 Node 的使用方式 和 UIView 有著 相似 的一些 代碼 特性; Node 會暴露 出 view 的一些基礎(chǔ)屬性 和 UIView 使用一樣, 這令我們的學(xué)習(xí)成本 有一定降低; 一些不會明顯暴露的屬性,也可以通過 node.view 來獲取到, 讓初次接觸 框架的人 比較容易上手。
其次, AsyncDisplayKit 是線程 絕對 安全的, 能保證 一些復(fù)雜的界面也穩(wěn)定 60fps 運行, 開發(fā)者不比去考慮 Image 解碼先蒋、文本大小和渲染等 帶來的線程堵塞, 有利于程序的性能優(yōu)化骇钦。 同事 使用 AsyncDisplayKit 來開發(fā), 代碼的條理 變得 清晰, 通過 ASCellNode 能夠完全和 ASTableNode 進行解耦來看, 各種優(yōu)點會讓程序變得更加容易維護。
于此同時, AsyncDisplayKit 帶來的負面影響也是不容忽視的;由于ASDK的基本理念是在需要創(chuàng)建UIView時替換成對應(yīng)的Node來獲取性能提升竞漾,因此對于現(xiàn)有代碼改動較大眯搭,侵入性較高,同時由于大量原本熟悉的操作變成了異步的业岁,對于一個團隊來說學(xué)習(xí)曲線也較為陡峭鳞仙。
AsyncDisplayKit 不支持 AutoLayout、AutoResizing笔时,也不支持StoryBoard棍好、IB。這 似乎 與 Apple 的理念相違背; Apple 是鼓勵使用 可視化進行編程的允耿。
綜合來分析, AsyncDisplayKit 是一個非常的不錯框架; 考慮到AsyncDisplayKit的種種好處借笙,非常推薦AsyncDisplayKit,當然還是僅限于用在比較復(fù)雜和動態(tài)的頁面中较锡。不需要也不可能將所有UIView都替換成其Node版本业稼。將注意力集中在可能造成主線程阻塞的地方,如tableView/collectionView蚂蕴、復(fù)雜布局的View低散、使用連續(xù)手勢的操作等等俯邓。