在這個(gè)AsyncDisplayKit 2.0
教程中厢洞,學(xué)習(xí)如何通過(guò)異步渲染的強(qiáng)大功能使用戶界面及其流暢的滾動(dòng)元潘。
AsyncDisplayKit是最初由Facebook的Paper
應(yīng)用程序開(kāi)發(fā)的UI框架穿撮。這就是Paper團(tuán)隊(duì)面臨的核心問(wèn)題之一:如何盡可能緩解主線程的壓力鹃彻?
現(xiàn)在,許多應(yīng)用程序的用戶體驗(yàn)很大程度上依賴于持續(xù)手勢(shì)和物理動(dòng)畫(huà)熙暴。至少,你的UI大部分依賴于某種形式的 scrollView
這些類型的用戶界面完全依賴于主線程且對(duì)主線程阻塞非常敏感秒咨。主線程阻塞將導(dǎo)致丟幀,降低用戶的體驗(yàn)计呈。
主線程開(kāi)銷較大的任務(wù)一般分為:
- 1: 計(jì)算尺寸和布局:比如
-heightForRowAtIndexPath:
砰诵,或者在UILbel
中調(diào)用-sizeThatFits
以及指數(shù)上升的AutoLayout
布局計(jì)算 - 2: 圖像解碼:想要在一個(gè)
imageView
中使用UIImage
,首先要進(jìn)行圖形解碼捌显。 - 3: 繪圖:復(fù)雜的文本以及手動(dòng)繪制漸變和陰影胧砰。
- 4: 對(duì)象生命周期:創(chuàng)建,操縱和銷毀系統(tǒng)對(duì)象(即創(chuàng)建一個(gè)UIView)
在正確使用中, AsyncDisplayKit
允許您在默認(rèn)情況下異步執(zhí)行所有計(jì)算苇瓣、布局和渲染尉间。在沒(méi)有任何額外優(yōu)化的情況下,應(yīng)用程序可以在主線程上完成的工作量大約減少一個(gè)數(shù)量級(jí)
除了這些性能優(yōu)勢(shì)击罪,酷炫的 AsyncDisplayKit
還為開(kāi)發(fā)者提供的便利接口哲嘲,用簡(jiǎn)潔的代碼就能完成復(fù)雜的功能。
在這兩部分 AsyncDisplayKit 2.0
教程中媳禁,你將掌握使用ASDK構(gòu)建一個(gè)實(shí)用的和動(dòng)態(tài)的應(yīng)用程序的所有要素眠副。 在第一部分中,你將要學(xué)習(xí)一些在你構(gòu)建應(yīng)用程序時(shí)可以用到的宏觀思想竣稽。 在第二部分中囱怕,你將學(xué)習(xí)如何構(gòu)建自己 node
的 subclass
霍弹,以及如何使用ASDK強(qiáng)大的布局引擎。為了更好的完成本教程娃弓,你需要會(huì)使用 Xcode
以及 熟悉 Objective-C
典格。
特別聲明:ASDK不兼容Interface Builder和AutoLayout,因此台丛,您將不會(huì)在本教程中使用它們耍缴,雖然ASDK完全支持Swift(除了ComponentKit),許多開(kāi)發(fā)者仍在使用.
Objective-C挽霉。免費(fèi)App排行榜前100大多數(shù)都沒(méi)有使用Swift(至少6個(gè)使用ASDK)防嗡。出于這些原因,本系列將重點(diǎn)介紹 Objective-C侠坎。話雖這么說(shuō)蚁趁,我們已經(jīng)包括了一個(gè)Swift版本的實(shí)例項(xiàng)目。(嘴上說(shuō)沒(méi)有实胸,代碼還是很誠(chéng)實(shí)的??~)
一: 開(kāi)始
請(qǐng)下載項(xiàng)目實(shí)例
該項(xiàng)目使用CocoaPods
來(lái)拉入AsyncDisplayKit
荣德。 因此,所以童芹,在正常的 CocoaPods 體系下,繼續(xù)打開(kāi)RainforestStarter.xcworkspace
而不是RainforestStarter.xcodeproj
鲤拿。
構(gòu)建并運(yùn)行以查看包含 UITableView 動(dòng)物列表的應(yīng)用程序假褪。如果你看過(guò)了代碼,AnimalTableController
你會(huì)發(fā)現(xiàn)這是一個(gè)正常且熟悉的UITableViewController
類近顷。
注意:確保在真機(jī)上運(yùn)行本教程中的代碼生音,而不是在模擬器中運(yùn)行。
向上滑動(dòng)你將看到幀數(shù)丟失引起的卡頓窒升。你不需要啟動(dòng)控制臺(tái)缀遍,以便能發(fā)現(xiàn)到這個(gè)應(yīng)用程序需要在性能方面上的一些優(yōu)化。
[圖片上傳失敗...(image-c8c6db-1547448512256)]
您可以通過(guò)AsyncDisplayKit的強(qiáng)大功能來(lái)解決這個(gè)問(wèn)題饱须。
二: 介紹ASDisplayNode
ASDisplayNode
是ASDK的核心類域醇,它只是一個(gè)類似于 MVC 中的 “View”。認(rèn)識(shí)一個(gè) node 的最佳方法是參照你已經(jīng)熟悉的UIViews
和CALayers
之間的關(guān)系蓉媳。
記住譬挚,iOS應(yīng)用程序中的所有在屏幕上的顯示都通過(guò)CALayer
對(duì)象表示的。UIViews
創(chuàng)建并且擁有一個(gè)底層的 CALayer
酪呻,并為他們添加觸摸處理和其他交互功能减宣。UIView 并不是 CALayer 的子類,它們環(huán)繞著一個(gè)圖層對(duì)象玩荠,擴(kuò)展了它的功能.
這種抽象的情況下擴(kuò)展 ASDisplayNode
您可以將它們視為包裝一個(gè)view
漆腌,就像在 view
包裝著layer
一樣贼邓。
通過(guò)常規(guī)視圖將哪些節(jié)點(diǎn)帶到表中的事實(shí)是它們可以在后臺(tái)隊(duì)列上創(chuàng)建和配置,并且默認(rèn)情況下同時(shí)呈現(xiàn)
幸運(yùn)的是闷尿,用于處理 Node 的 API 對(duì)于任何使用過(guò)的 UIViews
或者 CALayers
的人來(lái)說(shuō)應(yīng)該異常的熟悉塑径。所有 View 的屬性都可以等效為 Node 類。你可以訪問(wèn)基礎(chǔ)的 view 或者 layer 本身悠砚,就像是訪問(wèn) view.layer
一樣
三: 節(jié)點(diǎn)容器(The Node Containers)
雖然 Node 本身提供了巨大的性能改進(jìn)的可能晓勇,但真正的強(qiáng)大的是它們與四個(gè)容器類結(jié)合使用時(shí)產(chǎn)生的黑魔法。
這些類包括:
-ASViewController
:一個(gè)UIViewController
的子類灌旧,允許你提供要管理的 Node绑咱。
-ASCollectionNode
和ASTableNode
:與UICollectionView
和UITableView
的等價(jià),其子類實(shí)際上保留在底層枢泰。
ASCollectionNode
和ASTableNode
:與UICollectionView
和UITableView
的等價(jià)描融,其子類實(shí)際上是在框架維護(hù)的。ASPagerNode
:ASCollectionNode
的一個(gè)子類衡蚂,與UIKit的UIPageViewController
相比窿克,可以提供很好的刷卡性能。
說(shuō)得好毛甲,但真正的黑魔法來(lái)自ASRangeController
這些類用于影響所包含的 Node 的行為∧甓#現(xiàn)在,跟著我并把你們的腦袋放空吧~
四: TableNode
你要做的第一件事就是將當(dāng)前 TableView 替換為 TableNode玻募。這個(gè)沒(méi)什么難度只损。
4.1: tableNode來(lái)替換tableView
首先,導(dǎo)航到AnimalTableController.m七咧。 在該類的其他導(dǎo)入下方添加以下行:
#import <AsyncDisplayKit/AsyncDisplayKit.h>
導(dǎo)入ASDK為了使用框架跃惫。
然后,我們繼續(xù)艾栋,替換 tableView 的聲明屬性 :
@property (strong, nonatomic) ASTableNode *tableNode;
來(lái)替換
@property (strong, nonatomic) UITableView *tableView;
這將導(dǎo)致這個(gè)類中很多地方報(bào)錯(cuò)朽褪,但不要慌張阅懦!
別擔(dān)心诉探。這些錯(cuò)誤和警告將作為你實(shí)現(xiàn)相關(guān)功能的引導(dǎo)掏击。
-viewDidLoad
中的報(bào)錯(cuò)是理所當(dāng)然,因?yàn)?tableView 已經(jīng)被替換掉悼粮。我不會(huì)讓你通過(guò)tableNode
替換 所有的tableView
實(shí)例(我的意思是拇泣,查找和替換并非那么難),但是如果你做了矮锈,你會(huì)看到:
- 1: 你應(yīng)該為
ASTableNode
分配一個(gè)屬性霉翔。 - 2:
table Node
沒(méi)有調(diào)用-registerClass:forCellReuseIdentifier:
方法。 - 3: 你不能添加一個(gè)
node
到subview
此時(shí)苞笨,你應(yīng)該將 -viewDidLoad
中的方法替換為:
- (void)viewDidLoad {
[super viewDidLoad];
[self.view addSubnode:self.tableNode];
[self applyStyle];
}
這里要注意一個(gè)有趣的情況债朵,你調(diào)用的是 UIView
的一個(gè) -addSubnode:
方法子眶,該方法是通過(guò) category
添加到 UIView
上的,等效于:
[self.view addSubview:self.tableNode.view];
接下來(lái)序芦,修改 -viewWillLayoutSubviews
中的代碼:
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
self.tableNode.frame = self.view.bounds;
}
所有這一切都是用self.tableNode
替換self.tableView
來(lái)設(shè)置表的框架臭杰。
繼續(xù)修改 -applyStyle
方法中的代碼為:
- (void)applyStyle {
self.view.backgroundColor = [UIColor blackColor];
self.tableNode.view.separatorStyle = UITableViewCellSeparatorStyleNone;
}
這是唯一設(shè)置 table
的 separatorStyle
的一行代碼。注意 tableNode
的 view
是如何訪問(wèn) table
的 separatorStyle
屬性的谚中。ASTableNode
不會(huì)暴露所有UITableView
的的屬性渴杆,所以你必須通過(guò)tableNode
底層的 UITableView
實(shí)例去設(shè)置 UITableView
的特殊屬性。
然后宪塔,在 -initWithAnimals:
方法中添加磁奖。
_tableNode = [[ASTableNode alloc] initWithStyle:UITableViewStylePlain];
并且在 return 之前,調(diào)用:
[self wireDelegation];
這就會(huì)在初始化 AnimalTableController
的時(shí)候某筐,創(chuàng)建了一個(gè) tableNode
并且調(diào)用 -wireDelegation
方法 設(shè)置 tableNode
的 代理比搭。
4.2: 設(shè)置 TableNode 的 DataSource & Delegate
類似于 UITableView
,ASTableNode
也使用 DataSource
和 Delegate
來(lái)設(shè)置本身南誊。TableNode
的 ASTableDataSource
和 ASTableDelegate
protocols
非常類似于 UITableViewDataSource
和 UITableViewDelegate身诺。
事實(shí)上,雖然他們定義了一些完全相同的方法抄囚,如 -tableNode:numberOfRowsInSection:
霉赡,但兩組協(xié)議也不完全相同,因?yàn)?ASTableNode
行為和 UITableView
還以所有不同的幔托。
找到 -wireDelegation
方法穴亏, 并用 tableNode
替換 tableView
- (void)wireDelegation {
self.tableNode.dataSource = self;
self.tableNode.delegate = self;
}
現(xiàn)在, 你會(huì)收到警告, AnimalTableController
實(shí)際上不符合協(xié)議柑司。目前,AnimalTableController
僅遵循 UITableViewDataSource
和 UITableViewDelegate
協(xié)議锅劝。在下面的章節(jié)中攒驰,我們將遵循這些協(xié)議,使我們能夠使用 tableNode
的功能故爵。
4.3: 遵循 ASTableDataSource
在 AnimalTableController.m
開(kāi)頭的地方找到 AnimalTableController
的 DataSource
擴(kuò)展聲明:
@interface AnimalTableController (DataSource)<UITableViewDataSource>
@end
用 ASTableDataSource
替換 UITableViewDataSource
為:
@interface AnimalTableController (DataSource)<ASTableDataSource>
@end
現(xiàn)在玻粪,AnimalTableController
已經(jīng)遵循了 AnimalTableController
協(xié)議。本就該如此了诬垂。
導(dǎo)航到 AnimalTableController.m
的底部并找到 DataSource
category 的實(shí)現(xiàn)劲室。
首先,將 UITableViewDataSource
的-tableView:numberOfRowsInSection:
方法结窘,
更改為 ASTableDataSource
的版本很洋。
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section {
return self.animals.count;
}
接著, ASTableNodes
的 cells
會(huì)以不同于 UITableView
的方式返回隧枫。用下面的代碼替換 -tableView:cellForRowAtIndexPath:
以適應(yīng)新的規(guī)則喉磁。
// 1
- (ASCellNodeBlock)tableNode:(ASTableView *)tableView nodeBlockForRowAtIndexPath:(NSIndexPath *)indexPath {
// 2
RainforestCardInfo *animal = self.animals[indexPath.row];
// 3 return ASCellNodeBlock
return ^{
// 4
CardNode *cardNode = [[CardNode alloc] initWithAnimal:animal];
//You'll add something extra here later...
return cardNode;
};
}
讓我們回顧一下:
- 1: ASDK 中的
ASCellNode
等價(jià)于UITableViewCell
或者UICollectionViewCell
谓苟。要注意的是這個(gè)方法返回的是一個(gè)ASCellNodeBlock
,ASTableNode
維持著內(nèi)部所有的 Cell协怒,每個(gè)indexPath
對(duì)應(yīng)一個(gè)block
涝焙,并且隨時(shí)準(zhǔn)備進(jìn)行初始化。 - 2: 你的首要任務(wù)是通過(guò)數(shù)據(jù)模型構(gòu)建cell孕暇。這是非常重要的一步仑撞,要注意!你獲取數(shù)據(jù)后在 下面的
block
處理妖滔。不要在block
里引用indexPath,以防止block
運(yùn)行前的數(shù)據(jù)變動(dòng)隧哮。 - 3: 然后返回一個(gè) block,其返回值必須為
ASCellNode
- 4: 沒(méi)有必要擔(dān)心Cell的復(fù)用以及初始化一個(gè)
Cell
的方法铛楣。您可能會(huì)注意到您現(xiàn)在返回了CardNode
近迁,而不是CardCell
。
這讓我想到一個(gè)重要的點(diǎn)簸州〖撸或許你已經(jīng)了解到,使用 ASDK 不需要復(fù)用 cell岸浑,好吧搏存,我已經(jīng)說(shuō)了兩遍了,但能記住就好矢洲。請(qǐng)隨意刪除頂部kCellReuseIdentifier
的定義吧
static NSString *kCellReuseIdentifier = @"CellReuseIdentifier";
你不必再擔(dān)心 -prepareForReuse
了
4.3: 遵循 ASTableDelegate
在 AnimalTableController.m
頂部璧眠,找到以下Delegate類別接口聲明:
@interface AnimalTableController (Delegate)<UITableViewDelegate>
@end
用 ASTableDelegate
替換 UITableViewDelegate
@interface AnimalTableController (Delegate)<ASTableDelegate>
@end
現(xiàn)在 AnimalTableController
已經(jīng)遵循了 ASTableDelegate
,是時(shí)候做處理了读虏。在 AnimalTableController.m
底部找到 Delegate
分類的實(shí)現(xiàn)责静。
我們都知道,每個(gè) UITableView
至少都要提供一個(gè) -tableView:heightForRowAtIndexPath:
實(shí)現(xiàn)方法盖桥,因?yàn)槊總€(gè) cell
的高度都由代理計(jì)算和返回灾螃。
ASTableDelegate
中沒(méi)有 -tableView:heightForRowAtIndexPath:
。再 ASDK 中揩徊,所有的 ASCellNode
都負(fù)責(zé)確定自己的大小腰鬼。你可以選擇為單元格定義最小和最大尺寸,而不是提供靜態(tài)高度塑荒。這種情況下熄赡,你希望每個(gè)cell的高度至少為屏幕的 2/3。
現(xiàn)在不用擔(dān)心太多齿税,這個(gè)會(huì)在第二部分中介紹彼硫。
現(xiàn)在只需要替換 -tableView:heightForRowAtIndexPath:
為:
- (ASSizeRange)tableView:(ASTableView *)tableNode
constrainedSizeForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat width = [UIScreen mainScreen].bounds.size.width;
CGSize min = CGSizeMake(width, ([UIScreen mainScreen].bounds.size.height/3) * 2);
CGSize max = CGSizeMake(width, INFINITY);
return ASSizeRangeMake(min, max);
}
經(jīng)過(guò)我們的辛勤勞動(dòng),重新編譯、運(yùn)行項(xiàng)目乌助,看看發(fā)生了什么溜在。
真是一個(gè)流暢的 tableView!一旦你開(kāi)始做了他托,那就讓我們做的更好吧掖肋!
五: 無(wú)限滾動(dòng)
在大多數(shù)應(yīng)用中,服務(wù)器的數(shù)據(jù)個(gè)數(shù)往往會(huì)多于當(dāng)前 tableView 中顯示的數(shù)量赏参。這意味著志笼,你必須通過(guò)某些手段做無(wú)縫處理,以便用戶刷完當(dāng)前數(shù)據(jù)列表時(shí)從服務(wù)端加載新的數(shù)據(jù)把篓。
很多時(shí)候纫溃,這是通過(guò)手動(dòng)觀察滾動(dòng)視圖方法中的內(nèi)容偏移來(lái)處理 scrollViewDidScroll:
, 使用 ASDK, 有一種更具說(shuō)明性的處理方式韧掩。相反的紊浩,你可以預(yù)先確定好你需要加載的頁(yè)數(shù)。
你要做的第一件事是取消已經(jīng)存在的方法的注釋疗锐。在 AnimalTableController.m
的結(jié)尾坊谁,取消 Helpers
分類中的兩個(gè)方法。你可以認(rèn)為 -retrieveNextPageWithCompletion:
是你的網(wǎng)絡(luò)調(diào)用滑臊,而 -insertNewRowsInTableNode:
是個(gè)非常典型的再表中添加新的元素的方法口芍。
接下來(lái),在 -viewDidLoad
添加:
self.tableNode.view.leadingScreensForBatching = 1.0; // overriding default of 2.0
設(shè)置 leadingScreensForBatching
為 1.0 意味著當(dāng)用戶滑動(dòng)到列表只剩下一個(gè)屏幕的數(shù)據(jù)的到達(dá)結(jié)尾的時(shí)候雇卷,就會(huì)載入新的數(shù)據(jù)鬓椭。
繼續(xù),在 Delegate
分類中實(shí)現(xiàn):
- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode {
return YES;
}
此方法用于告知表是否應(yīng)該在此之后繼續(xù)請(qǐng)求新批次关划。如果您知道自己已經(jīng)到達(dá)API數(shù)據(jù)的末尾小染,請(qǐng)返回NO,不再發(fā)出請(qǐng)求贮折。
因?yàn)槟阆M麩o(wú)限滾動(dòng)裤翩,那就返回 YES,以確蓖鸦酰總是請(qǐng)求新的數(shù)據(jù)岛都。
接下來(lái)律姨,還要添加:
- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context {
//1
[self retrieveNextPageWithCompletion:^(NSArray *animals) {
//2
[self insertNewRowsInTableNode:animals];
//3
[context completeBatchFetching:YES];
}];
}
該方法在用戶滑動(dòng)到 table
的末端并且 -shouldBatchFetchForTableNode:
方法返回 YES 時(shí)被調(diào)用振峻。
讓我們回顧下上面的章節(jié):
- 1: 首先,你要請(qǐng)求新的 animals 數(shù)據(jù)來(lái)展示择份。通常是通過(guò) API 來(lái)獲取的一組array扣孟。
- 2: 完成后,用新下載的數(shù)據(jù)更新 tableView
- 3: 最后荣赶,確保
-completeBatchFetching:
返回的是YES凤价,即大功告成鸽斟。在完成操作之前,不會(huì)進(jìn)行新的數(shù)據(jù)請(qǐng)求利诺。
Build and Run富蓄,并且不停的滾呀滾。你將會(huì)看到不停的看到一只鳥(niǎo)慢逾,他們是無(wú)限的立倍。
六: 智能預(yù)加載
你在工作中是否曾經(jīng)遇到需要預(yù)先加載內(nèi)容到 scrollView 或者 pageView 控制器中?也許你正在處理一個(gè)充滿屏幕 image 侣滩,并且總是希望在接下來(lái)的幾張圖片加載時(shí)處于等待狀態(tài)口注,所以用戶很少看到占位符。
當(dāng)你再這樣的體系下工作時(shí)君珠,你很快就會(huì)意識(shí)到有很多問(wèn)題要考慮寝志。
- 1: 你占用了多少內(nèi)存
- 2: 你應(yīng)該提前多久加載內(nèi)容
- 3: 你決定什么時(shí)候忽略用戶的交互反映
并且當(dāng)你考慮到多個(gè)維度的內(nèi)容時(shí),將些問(wèn)題將會(huì)變得更加復(fù)雜策添。假設(shè)你有一個(gè)pageViewController
材部,里面每個(gè) viewController
都帶有一個(gè) collectionView
。現(xiàn)在舰攒,你就需要考慮如何在兩個(gè)方向上動(dòng)態(tài)加載內(nèi)容败富。同時(shí),還要對(duì)每個(gè)設(shè)備進(jìn)行優(yōu)化摩窃。
還記得告訴你 ASRangeController
是不重要的嗎兽叮?現(xiàn)在,這將是我們的重點(diǎn)猾愿。
在每個(gè)容器類中鹦聪,所有包含的 node 都有一個(gè)接口狀態(tài)的概念。在任何給定的時(shí)間蒂秘,一個(gè) node 可以是下面的任意組合:
- Preload Range(預(yù)載范圍):通常最遠(yuǎn)的范圍從可見(jiàn)區(qū)域泽本。這是當(dāng)cell的每個(gè) subNode (例如ASNetworkImageNode) 的內(nèi)容從外源加載,例如API和本地緩存姻僧。這與批量獲取時(shí)规丽,使用用模型對(duì)象代表cell本身形成對(duì)比。
- Display Range(顯示范圍):在這里進(jìn)行顯示任務(wù)撇贺,例如文本繪制和進(jìn)行圖像解碼赌莺。
- Visible Range(可見(jiàn)范圍):此時(shí),node 至少有一個(gè)像素在屏幕上松嘶。
這些范圍也適用于 screenfuls
的度量艘狭,并且可以使用 ASRangeTuningParameters
屬性輕松調(diào)整。
例如:你正在使用一個(gè) ASNetworkImageNode
在 gallery 的每個(gè)頁(yè)面中展示圖像,當(dāng)每個(gè)cell進(jìn)入 Preload Range
時(shí)巢音,會(huì)發(fā)送網(wǎng)絡(luò)請(qǐng)求遵倦,對(duì)進(jìn)入顯示范圍時(shí)檢索到的圖像進(jìn)行解碼.
一般來(lái)說(shuō),如果你不愿意官撼,你不必過(guò)多考慮這些范圍梧躺。內(nèi)置組件(如ASNetworkImageNode
和ASTextNode
)充分利用它們,這意味著默認(rèn)情況下您將看到巨大的好處
注意: 有件不明顯的事傲绣,這些 Ranges 不是堆棧的燥狰。相反,它們會(huì)在
Visible Range 上重疊和匯聚斜筐。如果將顯示和預(yù)取都設(shè)置為一個(gè)屏幕龙致,則它們將完全相同。通常數(shù)據(jù)需要存在才能顯示顷链,所以一般預(yù)取范圍應(yīng)該稍大一點(diǎn)目代。那么在 node 到達(dá)該范圍時(shí),就可以開(kāi)始顯示嗤练。
通常榛了,該范圍的前側(cè)大于后側(cè)。當(dāng)用戶改變他們的滾動(dòng)方向時(shí)煞抬,范圍的大小也反轉(zhuǎn)霜大,以便有利于用戶實(shí)際移動(dòng)的內(nèi)容。
七: Node接口的狀態(tài)回調(diào)
你可能會(huì)疑惑:這些 Ranges
是如何正確工作的革答?很高興你這樣問(wèn)~
系統(tǒng)中的每個(gè) node
都有一個(gè) interfaceState
屬性战坤,是一個(gè)帶有字段((NS_OPTION)ASInterfaceState
類型。 ASRangeController
負(fù)責(zé)管理 ASCellNode
在 scrolView
上的移動(dòng)残拐,每個(gè) subNode
都由一個(gè) interfaceState
屬性做對(duì)應(yīng)的更新途茫。這意味著即使時(shí) tree 中最深的 nodes 也可以相應(yīng) interfaceState
的變化。
幸運(yùn)的是溪食,我們很少需要直接去操作 node
的 interfaceState
上的 二進(jìn)制位囊卜。更常見(jiàn)的做法時(shí),你只需要對(duì)某 node
的特定的狀態(tài)進(jìn)行更改错沃。這就是接口的狀態(tài)回調(diào)栅组。
7.1: Node 命名
為了看到一個(gè) node
的各種狀態(tài),給它命名時(shí)很有必要的枢析。這樣玉掸,你就可以監(jiān)測(cè)每個(gè) node
的數(shù)據(jù)加載、內(nèi)容成登疗、屏幕展示以及所以的事情排截。
回到代碼 -tableNode:nodeBlockForRowAtIndexPath:
,添加一句注釋
//You'll add something extra here later...
在它的下面,給 cardNode
添加一個(gè) debugName:
cardNode.debugName = [NSString stringWithFormat:@"cell %zd", indexPath.row];
現(xiàn)在辐益,您將能夠跟蹤cell在范圍內(nèi)的進(jìn)展断傲。
7.2: 監(jiān)聽(tīng)Cells
進(jìn)入 CardNode_InterfaceCallbacks.m
中,你可以找到六種追蹤 node
在 ranges
中的狀態(tài)的方法智政。取消注釋认罩,Build and Run
。打開(kāi)你的控制臺(tái)续捂,然后慢慢滑動(dòng) table垦垂。對(duì)照你的滑動(dòng),觀察cell在對(duì)應(yīng)的狀態(tài)變化牙瓢。
注意: 大多數(shù)情況下劫拗,你只要關(guān)心
-didEnterVisibleState
或-didExitVisibleState
方法對(duì)ASInterfaceState
的改變》耍或者說(shuō)页慷,已經(jīng)為你做好了許多引擎。你可以查看ASNetworkImageNode
中的代碼胁附,看看你集成的通過(guò)Preload
和Display
狀態(tài)實(shí)現(xiàn)的功能酒繁。 所有node
網(wǎng)絡(luò)圖片的請(qǐng)求和解碼,以及內(nèi)存的釋放都是自動(dòng)完成控妻,不費(fèi)吹灰之力州袒。
八: 智能預(yù)加載(續(xù))
在 2.0 版本中,已經(jīng)介紹了多個(gè)維度上智能與加載的概念弓候。假設(shè)你有一個(gè)豎直滾動(dòng)的tableView郎哭,在其中某些Cell包含了水平滾動(dòng)的 collectionView
雖然這個(gè)集合現(xiàn)在在技術(shù)上處于可見(jiàn)區(qū)域,但您不希望預(yù)先加載整個(gè)集合菇存。相反彰居,兩個(gè)滾動(dòng)視圖都有自己的ASRangeController
,并帶有可單獨(dú)配置的范圍調(diào)整參數(shù)撰筷。
8.1: 來(lái)到二次元
現(xiàn)在陈惰,你已經(jīng)有了完整的 AnimalTableController
, 你可以把它做為 ASPagerNode
的一個(gè)page。
項(xiàng)目已經(jīng)提前寫(xiě)好了控制器的代碼毕籽,首先進(jìn)入 AppDelegate.m
找到 -installRootViewController
的下面代碼:
AnimalTableController *vc = [[AnimalTableController alloc]
initWithAnimals:[RainforestCardInfo allAnimals]];
替換為:
AnimalPagerController *vc = [[AnimalPagerController alloc] init];
然后抬闯,跳到 AnimalPagerController.m
在 -init
方法中添加創(chuàng)建 pager
方法以及 dataSource
的數(shù)據(jù)源:
_pagerNode = [[ASPagerNode alloc] init];
_pagerNode.dataSource = self;
pagerNode 是 ASCollectionNode
的子類,使用方法與 UIPageViewController
一樣关筒。API 實(shí)際上比 UIPageViewController
要簡(jiǎn)單的多溶握。
接下來(lái)要實(shí)現(xiàn) pager 的 dataSource
方法,在底部找到 ASPagerDataSource
分類.
首先蒸播,告訴 pager 有幾個(gè)頁(yè)面睡榆。實(shí)際上當(dāng)前的 animal 數(shù)組中有三組不同動(dòng)物萍肆,我們需要重寫(xiě) -numberOfPagesInPagerNode:
方法:
- (NSInteger)numberOfPagesInPagerNode:(ASPagerNode *)pagerNode {
return self.animals.count;
}
然后,你需要實(shí)現(xiàn) -pagerNode:nodeAtIndex
方法胀屿,類似于先前實(shí)現(xiàn)的 ASTableNode
的 dataSource
方法塘揣。
- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index {
// 1
NSArray *animals = self.animals[index];
// 2
ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^UIViewController * _Nonnull{
return [[AnimalTableController alloc] initWithAnimals:animals];
} didLoadBlock:nil];
return node;
}
我們來(lái)總結(jié)下這部分:
- 1: 盡管這個(gè)版本中沒(méi)有進(jìn)行模塊化分,但是首先獲取數(shù)據(jù)模型是個(gè)好習(xí)慣宿崭。
- 2: 這一次亲铡,你使用的正是強(qiáng)大的 -initWithViewControllerBlock: 構(gòu)造器。你所要做的就是返回一個(gè)block葡兑,這個(gè) block 返回你提前設(shè)置好的
tableNodeController
奖蔓,它將自動(dòng)展示在pager 的 頁(yè)面中。真是太酷了??~
一旦你添加了這個(gè)方法讹堤,你將擁有一個(gè)完整功能的 Pagar吆鹤,其中的 cell 是從你原先創(chuàng)建的 tableNodeController
生成的。現(xiàn)在洲守,就可以在用戶的垂直和水平滑動(dòng)下檀头,充分發(fā)揮二維預(yù)加載的功能!
要查看這個(gè) AsyncDisplayKit 2.0 教程完整的項(xiàng)目岖沛,點(diǎn)擊這里進(jìn)行下載暑始。如果你想查看swift版本,這里也有婴削。
準(zhǔn)備好之后廊镜,請(qǐng)轉(zhuǎn)到該項(xiàng)目的第2部分,了解 AsyncDisplayKit 2.0 引入的強(qiáng)大的新的布局系統(tǒng)唉俗。
如果你想先進(jìn)行深入了解嗤朴,你可以閱讀 AsyncDisplayKit主頁(yè) 的文檔。Scott Goodson(AsyncDisplayKit的原創(chuàng)作者)也有幾個(gè)你可能會(huì)感興趣的話題虫溜。最近的話題很好的概述了一些框架對(duì)處理大圖片存在問(wèn)題的的嘗試雹姊。
你可能會(huì)對(duì) Paper的構(gòu)建感興趣。雖然當(dāng)時(shí)并沒(méi)有開(kāi)源衡楞,并且有許多地方發(fā)生了變化吱雏,但看到這一切的開(kāi)始還是挺有意思的。
這里有一個(gè) public Skack channel,歡迎來(lái)提問(wèn)~
著作權(quán)聲明
AsyncDisplayKit 2.0 Tutorial: Getting Started
轉(zhuǎn)載AsyncDisplayKit 2.0 教程:入門(mén)「譯」
相關(guān)資料:
iOS 開(kāi)發(fā)一定要嘗試的 Texture(ASDK)