AsyncDisplayKit入门指南

jopen 9年前

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风格)随机播放按钮。

1-shuffle-crop

我们的视图控制器代码将和下面的差不多:

- (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