AsyncDisplayKit入門指南
非死book前兩天發布了其iOS UI框架AsyncDisplayKit(ASDK)的1.0正式版,這個框架被用于非死book自家的應用Paper,能夠提高UI的流暢性并縮短響應時間。
下載和使用
你可以使用CocoaPods來安裝AsyncDisplayKit,在Podfile中添加:
pod 'AsyncDisplayKit'
OC中導入框架header,如果用Swift則可以創建Objective-C bridging header:
#import <AsyncDisplayKit/AsyncDisplayKit.h>
概述
AsyncDisplayKit(ASDK)的基本單元是node,ASDisplayNode是UIView之上的抽象層,因此同時也是 CALayer的抽象層。和只能被用在主線程的視圖不同,nodes是線程安全的:你能并行的實例化并設置整個node層級,并且在后臺線程里運行。
為了保證它的用戶界面的流暢和響應,你的app渲染幀率應該和iOS基準保持同步,即60幀每秒。這意味著主線程對每一幀有60分之一的時間來推送,這大約是16毫秒,需要在如此短時間內來執行所有的布局和繪制的代碼。而由于系統的性能開銷,在導致丟幀之前,你的代碼通常只有不到10毫秒的時間來執行。
AsyncDisplayKit讓你將圖像解碼、調整文字大小并渲染,以及其他高耗時的UI操作移出主線程。當然它還有其他的一些功能,你可以在官方文檔中探索。
作為視圖直接替代的Node
如果你之前處理過視圖,那么你已經知道如何使用node了。Node的API也類似于UIView,不過更方便。比如,你能直接讀取公共的CALayer屬性。添加一個node到現有的視圖或層級,使用node.view或node.layer。
AsyncDisplayKit包括一些強力的組件:
- ASDisplayNode. UIView的副本 — 一個子類,用來自定義node。
- ASControlNode. 類似于UIControl — 用來制作按鈕的子類。
- ASImageNode. 類似于UIImageView — 異步的解碼圖像資源。
- ASTextNode. 類似于UITextView — 基于TextKit構建,支持富文本的全部特性。
- ASTableView. UITableView子類,用于支持node。
你可以將這些用作UIKit副本的直接替代。即便ASDK在完整的基于node的層級下工作十分高效,使用node替代獨立視圖能夠更加的提高性能。
讓我們來看一個例子。
我們一開始在主線程中異步的使用node——和你平常使用視圖的方式一樣。我們的代碼和自定義視圖控制器-loadView的實現差不多:
_imageView = [[UIImageView alloc] init]; _imageView.image = [UIImage imageNamed:@"hello"]; _imageView.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f); [self.view addSubview:_imageView];
我們能使用如下基于node的代碼來替代它:
_imageNode = [[ASImageNode alloc] init]; _imageNode.backgroundColor = [UIColor lightGrayColor]; _imageNode.image = [UIImage imageNamed:@"hello"]; _imageNode.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f); [self.view addSubview:_imageNode.view];
這并沒有利用ASDK的異步調整大小和布局的功能,但已經有所改進了。第一段代碼在主線程解碼hello.png,第二段則在后臺線程解碼圖像,并可能利用不同的CPU核心。
(注意我們在node中設置了占位符的背景顏色,它會占據屏幕直到真正的內容出現。這種做法對于圖像很好,但不太適用于文字——人們期望文字能直接展示,圖片則允許有加載延遲。我們后面會討論如何改進的技術。)
node按鈕
ASImageNode和ASTextNode都繼承了ASControlNode,所以你能將它們當做按鈕使用。假設我們在開發音樂播放器,并且希望添加(非擬物化的,iOS 7風格)隨機播放按鈕。
我們的視圖控制器代碼將和下面的差不多:
- (void)viewDidLoad { [super viewDidLoad]; // attribute a string NSDictionary *attrs = @{ NSFontAttributeName: [UIFont systemFontOfSize:12.0f], NSForegroundColorAttributeName: [UIColor redColor], }; NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"shuffle" attributes:attrs]; // create the node _shuffleNode = [[ASTextNode alloc] init]; _shuffleNode.attributedString = string; // configure the button _shuffleNode.userInteractionEnabled = YES; // opt into touch handling [_shuffleNode addTarget:self action:@selector(buttonTapped:) forControlEvents:ASControlNodeEventTouchUpInside]; // size all the things CGRect b = self.view.bounds; // convenience CGSize size = [_shuffleNode measure:CGSizeMake(b.size.width, FLT_MAX)]; CGPoint origin = CGPointMake(roundf( (b.size.width - size.width) / 2.0f ), roundf( (b.size.height - size.height) / 2.0f )); _shuffleNode.frame = (CGRect){ origin, size }; // add to our view [self.view addSubview:_shuffleNode.view]; } - (void)buttonTapped:(id)sender { NSLog(@"tapped!"); }
這段代碼能正常工作。不幸的是,這個按鈕只有14?個像素點高,離標準的44×44最小點擊尺寸相距甚遠,因此很難點擊到。我們能夠通過為 text node創建子類,并且覆蓋-hitTest:withEvent:
來解決這個問題。我們甚至能強制text view在布局中的最低高度,但如果有更優雅的解決辦法不是更好嗎?
// size all the things /* ... */ // make the tap target taller CGFloat extendY = roundf( (44.0f - size.height) / 2.0f ); _shuffleNode.hitTestSlop = UIEdgeInsetsMake(-extendY, 0.0f, -extendY, 0.0f);
就是這樣!Hit-test slops在所有node中都能正常工作,并且是一個展示這個新的抽象層能干什么的極好的示例。
后面還會講如何自定義node、異步的工作方式、最大限度的利用AsyncDisplayKit等,感興趣的可以查看官方文檔。
來自:http://idlelife.org/archives/733