Teture的便捷性

Hit Test Slop

ASDisplayNode提供了UIEdgeInset类型的属性,当设置为非0的inset时,提高点击命中范围,使其更容易点击或执行手势

因为ASDisplayNode是所有node的基类,因此所有的texturenode都有这个属性

注意

这个属性影响了-hitTest-pointInside,所以在重写这个方法时 建议调用super

节点捕获触摸事件的能力受到父节点的边界和父级hitTestSlop属性的限制。如果你想让子项的hitTestSlop扩展到父项的边界之外,只需要扩展父节点的hitTestSlop即可

示例:
当有一个文本节点作为按钮时,通常,文本节点高度通常不会达到要求的最低44的点击高度,此时,可以计算出区域差,并对标签应用inset来增加点击区域

ASTextNode *textNode = [[ASTextNode alloc] init];
CGFloat padding = (44.0 - button.bounds.size.height)/2.0;
textNode.hitTestSlop = UIEdgeInsetsMake(-padding, 0, -padding, 0);

批量获取API

Texture的批量获取API,用于批量获取数据块。这个通常在会写在-scrollViewDidScroll:方法中,但是Texture提供了更结构化的机制

默认,当用户滑动table或者collection,当那里距离屏幕底部2个屏幕远时,会试着获取更多的数据。我们可以通过调整ASTableView或者ASCollectionViewleadingScreensForBatching属性

tableNode.view.leadingScreensForBatching = 3.0;  // overriding default of 2.0

代理方法

实现该代理方法 决定是否需要加载新内容数据

- (BOOL)shouldBatchFetchForTableNode:(ASTableNode *)tableNode
{
  if (_weNeedMoreContent) {
    return YES;
  }

  return NO;
}

当用户滚动到批量获取范围时,会调用该方法,我们通常根据是否还有数据需要获取来决定是否需要会进行批量获取请求,当返回YES时 就会进行接下来的步骤:

-tableNode:willBeginBatchFetchWithContext:
-collectionNode:willBeginBatchFetchWithContext:

我们在这个方法中 执行数据获取(从网络或者本地数据库获取数据)

注意

这个方法总是在后台线程中调用,意味着如果需要工作在主线程中,就需要dispatch其到主线程

- (void)tableNode:(ASTableNode *)tableNode willBeginBatchFetchWithContext:(ASBatchContext *)context 
{
  // Fetch data most of the time asynchronously from an API or local database
  NSArray *newPhotos = [SomeSource getNewPhotos];

  // Insert data into table or collection node
  [self insertNewRowsInTableNode:newPhotos];

  // Decide if it's still necessary to trigger more batch fetches in the future
  _stillDataToFetch = ...;

  // Properly finish the batch fetch
  [context completeBatchFetching:YES];
}

当完成数据获取时,需要调用-completeBatchFetching:方法传递参数YES来通知已经结束数据获取。这确保了批量获取机制可以同步进行,并且下次批量获取可以发生了。只有传递YES,context才知道可以尝试另外一批更新。

自动节点管理

自动节点管理

当我们需要使用Layout Transition api时,就需要启用自动节点管理功能。然而,不需要动画的功能,也会从代码量的减小中受益

启用了自动节点管理功能,说明我们不再需要再调用addNode或者removeNode方法。节点的存在与否完全由layoutSpecThatFits:方法决定

更新ASLayoutSpec

当需要更改ASLayoutSepec时,我们需要手动调用setNeedLayout。这个等效于Transition Layout API中的transitionLayout: duration:0

正确构造的ASLayoutSpec知道需要添加、删除或设置动画的子节点。

注意

在启用的自动节点管理后,就不能再调用addSubnode:removeFromSupernode方法

反转

ASTableNideASCollectionNide具有BOOL类型的反转属性,当设置为YES时,将自动反转内容可以自下向上布局(即indexPath为(0,0)的位于底部),这对于聊天应用是十分方便的,而且仅需要一个属性

 CGFloat inset = [self topBarsHeight];
 self.tableNode.view.contentInset = UIEdgeInsetsMake(0, 0, inset, 0);
 self.tableNode.view.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, inset, 0);
  

图像修改block

大多数时候,我们在主线程进行大量修改图像外观的操作,我们当然希望将其移动到后台

通过给imageNode设置imageModificationBlock,定义一系列的转换,这些转换与imageNode上设置的图像异步发生

//举例
_backgroundImageNode.imageModificationBlock = ^(UIImage *image) {
    UIImage *newImage = [image applyBlurWithRadius:30
        tintColor:[UIColor colorWithWhite:0.5 alpha:0.3]
        saturationDeltaFactor:1.8
        maskImage:nil];
    return newImage ?: image;
};

//some time later...

_backgroundImageNode.image = someImage;

此时,每当把照片分配给imageNode前,都需要进行异步处理

添加图像效果

利用imageModificationBlock给图像添加效果是很有效的,当提供了block时,可以再显示阶段对图像执行绘制操作。由于显示是在后台执行的,因此不会阻塞主线程

imageModificationBlock可以非常方便的用于添加各种图像效果

举个例子:
我们有一个图像节点,并且imageNode需要被切圆角。我们可以提供一个imageModificationBlock,可以方便的将传入的图像切圆并且返回

- (instancetype)init
{
// ...
  _userAvatarImageNode.imageModificationBlock = ^UIImage *(UIImage *image) {
    CGSize profileImageSize = CGSizeMake(USER_IMAGE_HEIGHT, USER_IMAGE_HEIGHT);
    return [image makeCircularImageWithSize:profileImageSize];
  };
  // ...
}

//绘图代码抽象为UIImage的匪类中
@implementation UIImage (Additions)
- (UIImage *)makeCircularImageWithSize:(CGSize)size
{
  // make a CGRect with the image's size
  CGRect circleRect = (CGRect) {CGPointZero, size};

  // begin the image context since we're not in a drawRect:
  UIGraphicsBeginImageContextWithOptions(circleRect.size, NO, 0);

  // create a UIBezierPath circle
  UIBezierPath *circle = [UIBezierPath bezierPathWithRoundedRect:circleRect cornerRadius:circleRect.size.width/2];

  // clip to the circle
  [circle addClip];

  // draw the image in the circleRect *AFTER* the context is clipped
  [self drawInRect:circleRect];

  // get an image from the image context
  UIImage *roundedImage = UIGraphicsGetImageFromCurrentImageContext();

  // end the image context since we're not in a drawRect:
  UIGraphicsEndImageContext();

  return roundedImage;
}
@end

Placeholders

任何ASDisplayNode的子类都实现了-placeholderImage方法,提供了一个覆盖内容的的占位图,直到node内容显示。
通过设置.placeholderEnabled = YES以及可选属性.placeholderFadeDuration
对于image drawing,使用node的.calculateSize属性

注意

因为placeholderImage函数有可能在后台调用,因此我们需要保证其线程安全。注意,利用 -[UIImage imageNamed:]使用image asset不是线程安全,可以代替的使用-[UIImage imageWithContentsOfFile:]

Texture中的UIImage + ASConvenience类别方法是创建占位符图像的理想资源,包括圆形,矩形,单色或简单的方角图像。

.neverShowPlaceholders

ASNetworkImageNode also have Default Images

对于ASNetworkImageNode除了展位图像还有 .defaultImage属性。虽然占位符是暂时的,但如果图像节点.URL属性是nil或者URL加载失败,则默认图像将保留。建议使用默认图像

Accessibility

UICollectionViewCell的互通性

Texture提供了UICollectionViewCellASCellNodes的互通性

注意

UIKit中的cell即使混杂在ASCollectionNode中,也并不会有类似ASCellNodes的便捷收益(类似,预加载,异步布局,异步绘图等)