Nodes

ASDisplayNode

ASDisplayNode是UIView和CALayer的主要抽象。与UIView创建和持有CALayer相同,Node创建和持有UIView

ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.backgroundColor = [UIColor orangeColor];
node.bounds = CGRectMake(0, 0, 100, 100);
NSLog(@"Underlying view: %@", node.view);

node拥有与UIView相同的所有属性,view和layer的所有属性都会映射到node,非常方便我们使用

ASDisplayNode *node = [[ASDisplayNode alloc] init];
node.clipsToBounds = YES;       // not .masksToBounds
node.borderColor = [UIColor blueColor];  //layer name when there is no UIView equivalent
NSLog(@"Backing layer: %@", node.layer);

当与node container一起使用时,node的属性将会在后台线程设置,

视图包装器

有时,我们提供一个视图作为node的backing view。这个视图在block中提供,block中返回视图并进行保存。此时,这些node的展示会同步发生,因为只有node中的_ASDisplayView才会异步显示,在普通包装的UIView并不会异步显示

ASDisplayNode *node = [ASDisplayNode alloc] initWithViewBlock:^{
    SomeView *view  = [[SomeView alloc] init];
    return view;
}];

我们通常使用这个方法将UIView的子类转换为ASDisplayNode子类,来包装现有视图

ASCellNode

是Texture中的cell类,ASCellNode可以和ASTableNodesASCollectionNodesASPageNodes一起使用

使用cellNode的三种方式

子类化

与子类化ASDisplayNode几乎相同,需要使用以下方法:

  • -init
  • -layoutSpecThatFits
  • -didLoad
  • -layout

使用ASViewController初始化

将视图控制器作为视图放入cell的scroll node或者page node

举个例子:
编写一个用于管理ASTableNode的视图控制器。将该表作为ASPagenode的页面,使用initWithViewControllerBlock

- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
{
    NSArray *animals = self.allAnimals[index];
    
    ASCellNode *node = [[ASCellNode alloc] initWithViewControllerBlock:^UIViewController * _Nonnull{
        return [[AnimalTableNodeController alloc] initWithAnimals:animals];
    } didLoadBlock:nil];
    node.style.preferredSize = pagerNode.bounds.size;
    return node;
}

注意

使用这个方式设置的node,需要设置其.style.preferredSize

使用UIView和CALayer初始化

当如果有UIView或者CALayer子类 作为cell

- (ASCellNode *)pagerNode:(ASPagerNode *)pagerNode nodeAtIndex:(NSInteger)index
{
    NSArray *animal = self.animals[index];
    
    ASCellNode *node = [[ASCellNode alloc] initWithViewBlock:^UIView * _Nonnull{
        return [[SomeAnimalView alloc] initWithAnimal:animal];
    }];

    node.style.preferredSize = pagerNode.bounds.size;
    
    return node;
}

虽然可以这么做 但是推荐将现有的UIView类转为ASCellNode的子类 获取异步显示的优势

Never Show Placeholders

通常,如果cell在其到达屏幕之前仍为display完成,将会显示占位符直到绘制内容完成

但是,当如果我们不想要看到placeholder内容,可以将其属性neverShowPlaceholders设置为YES

node.neverShowPlaceholders = YES;

将其设置为yes,则cell的主线程将会被阻塞,直到cell显示完成为止。

UITableViewCell的特殊属性

ASCellNode中也存在和UITableViewCell中的类似selectionStyle、accessoryType和seperatorInset等我们偶尔可能使用的属性

ASButtonNode

类似UIButton继承自UIControl,而ASButtonNode也继承自ASControlNode

control state

[buttonNode setTitle:@"Button Title Normal" withFont:nil withColor:[UIColor blueColor] forState:ASControlStateNormal];

//可以通过设置Attribute  设置更多自定义属性
[self.buttonNode setAttributedTitle:attributedTitle forState:ASControlStateNormal];

Target-Action

[buttonNode addTarget:self action:@selector(buttonPressed:) forControlEvents:ASControlNodeEventTouchUpInside];

Content Alignment

ASButtonNode提供了contentVerticalAlignment和contentHorizontalAlignment属性,可以轻松设置对齐方式

self.buttonNode.contentVerticalAlignment = ASVerticalAlignmentTop;
self.buttonNode.contentHorizontalAlignment = ASHorizontalAlignmentMiddle;

注意

ASTextNode

Texture中的主要文本节点,通常在使用UILabel的情况下使用。拥有完整的富文本支持,并且继承自ASControlNode,

使用

ASTextNode的接口对于使用UILabel的人来说非常熟悉,第一个不同的地方在于node仅使用attribute而不能使用纯文字字符

NSDictionary *attrs = @{ NSFontAttributeName: [UIFont fontWithName:@"HelveticaNeue" size:12.0f] };
NSAttributedString *string = [[NSAttributedString alloc] initWithString:@"Hey, here's some text." attributes:attrs];

_node = [[ASTextNode alloc] init];
_node.attributedText = string;

截断字符串

当显示文本的空间小于要显示的文本必须空间,将会尽可能多的显示文本,并且所有被截断字符串将会被截断字符串替换。

_textNode = [[ASTextNode alloc] init];
_textNode.attributedText = string;
_textNode.truncationAttributedText = [[NSAttributedString alloc]

默认情况下,截断字符串为...

Link Attributes

为了将文本指定为链接,需要将linkAttribute数组设置为字符串数组,将该数组用作属性字符串中链接的键。在设置attribute时,将这些键指向响应的NSURL

_textNode.linkAttributeNames = @[ kLinkAttributeName ];

NSString *blurb = @"kittens courtesy placekitten.com \U0001F638";
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:blurb];
[string addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"HelveticaNeue-Light" size:16.0f] range:NSMakeRange(0, blurb.length)];
[string addAttributes:@{
                      kLinkAttributeName: [NSURL URLWithString:@"http://placekitten.com/"],
                      NSForegroundColorAttributeName: [UIColor grayColor],
                      NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle | NSUnderlinePatternDot),
                      }
              range:[blurb rangeOfString:@"placekitten.com"]];
_textNode.attributedText = string;
_textNode.userInteractionEnabled = YES;

ASTextNodeDelegate

实现ASTextNodeDelegate允许类对与文本相关联的各种事件做出反应

例如点击事件:

- (void)textNode:(ASTextNode *)richTextNode tappedLinkAttribute:(NSString *)attribute value:(NSURL *)URL atPoint:(CGPoint)point textRange:(NSRange)textRange
{
  // the link was tapped, open it
  [[UIApplication sharedApplication] openURL:URL];
}

类似的:

– textNode:longPressedLinkAttribute:value:atPoint:textRange:

– textNode:shouldHighlightLinkAttribute:value:atPoint:

– textNode:shouldLongPressLinkAttribute:value:atPoint:

具有行间距的文本导致不正确的最大行数

当多行文本中使用NSParagraphStyle修改了非默认的lineSpacing,那么就会导致设置的最大行数不正确

NSString *someLongString = @"...";

NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineSpacing = 10.0;

UIFont *font = [UIFont fontWithName:@"SomeFontName" size:15];

NSDictionary *attributes = @{
    NSFontAttributeName : font,
    NSParagraphStyleAttributeName: paragraphStyle
};

ASTextNode *textNode = [[ASTextNode alloc] init];
textNode.maximumNumberOfLines = 4;
textNode.attributedText = [[NSAttributedString  alloc] initWithString:someLongString

为了暂时解决此问题 可以通过设置truncationModeNSLineBreakByTruncatingTail

ASTextNode *textNode = [[ASTextNode alloc] init];
textNode.maximumNumberOfLines = 4;
textNode.truncationMode = NSLineBreakByTruncatingTail;
textNode.attributedText = [[NSAttributedString  alloc] initWithString:someLongString

ASImageNode

等效于UIImageView。最基本的区别在于图像默认情况下是异步解码的。并且还有其他功能,例如支持GIF和imageModificationBlocks

基础使用

使用方式与imageView 基本相同

ASImageNode *imageNode = [[ASImageNode alloc] init];
imageNode.image = [UIImage imageNamed:@"someImage"];
imageNode.contentMode = UIViewContentModeScaleAspectFill;

图像变换和效果

通过为imageNode设置imageModificationBlock,可以定义需要在imagenode上设置的任何需要图像异步发生的转换,包括圆角、添加边框、覆盖花纹等

图像裁剪

image的默认mode为UIViewContentModeScaleAspectFill,图片将会放大填充内容区域,并且裁剪掉超出的内容部分。

我们可以通过设置cropRect来移动图片,其为单元矩阵,默认值为(0.5,0.5,0,0),要显示左侧内容,可将其x设置为0,以为这图片原点从{0, 0}开始,而非默认值

强制放大

默认情况下,图像过小而无法放入已设置的imageNode的边界时,将不会在CPU上放大图像。

如果您想更改此事实,可以将forceUpscaling设置为YES。这样做意味着您每次使用小于目标位置的图像时,您的应用程序都会占用更多内存

检测图像缩放

通过使用pixel scaling tool可以方便的查看图片放大或者缩小了多少

ASNetworkImageNode

当需要显示远程的图像时,可以使用ASNetworkImageNode,需要做的就是将.URL属性设置为合适的NSURL实例,图像将会异步加载并呈现

ASNetworkImageNode *imageNode = [[ASNetworkImageNode alloc] init];
imageNode.URL = [NSURL URLWithString:@"https://someurl.com/image_uri"];

Network Image 布局

因为ASNetworkImageNode并未限制内容大小,因此必须指定layout的大小

  1. style.preferredSize

当我们想要使用标准尺寸时

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
{
    imageNode.style.preferredSize = CGSizeMake(100, 200);
    ...
    return finalLayoutSpec;
}
  1. ASRatioLayoutSpec

使用ASRatioLayoutSpec为图像分配一个比例,当图像加载完成后 将会保持该比例

- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
{
    CGFloat ratio = 3.0/1.0;
    ASRatioLayoutSpec *imageRatioSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:self.imageNode];
    ...
    return finalLayoutSpec;
}

引擎组件

如果选择没有包括PINRemoteImagePINCache,那么将失去对jpeg的渐进支持,并且需要自定义实现ASImageCacheProtocol的缓存

渐进式JPEG支持

由于包含了PINRemoteImage,NetworkImageNode现在支持加载渐进式的JPEG。意味着,如果服务器提供了这种jpeg,则图像会将以较低的质量快速显示,并且随着加载更多的数据而扩大

启用渐进加载只需要设置shouldRenderProgressImagesYES

networkImageNode.shouldRenderProgressImages = YES;

注意

这种方式是使用一张逐渐加载的图像。如果服务器只能使用常规的JPEG,但是可以提供多个版本来提高质量,那么应该使用ASMultiplexImageNode

自动缓存

使用PINCache自动缓存网络图像

GIF支持

ASNetworkImageNode通过PINRemoteImage的beta版PINAnimatedImage提供GIF支持。注意!除非shouldCacheImage设置为NO,否则该支持将不适用于本地文件。

ASVideoNode

方便我们在app中展示视频

使用ASVideoNode就会连接AVFoundation,因为在内部底层中使用了AVPlayerLayerAVFoundataion

基础使用

最简单的方式就是设置一个AVAsset资源

ASVideoNode *videoNode = [[ASVideoNode alloc] init];

AVAsset *asset = [AVAsset assetWithURL:[NSURL URLWithString:@"http://www.w3schools.com/html/mov_bbb.mp4"]];
videoNode.asset = asset;

自动播放、自动重复、自动静音

通过一些简单的BOOL属性,设置视频播放

//进入可见范围后自动播放
videoNode.shouldAutoplay = YES;
//视频无限循环播放
videoNode.shouldAutorepeat = NO;
//将静音设置为YES 将会关闭视频声音
videoNode.muted = YES;

占位图像

因为video node继承自ASNetworkImageNode,因此可以使用URL属性设置其占位图像。加入决定不这么做,则视频的第一针将会自动解码并作为占位图像

ASVideoNode Delegate

有很多delegate可以对视频事件作出响应,

例如,当播放状态改变时:

- (void)videoNode:(ASVideoNode *)videoNode willChangePlayerState:(ASVideoNodePlayerState)state toState:(ASVideoNodePlayerState)toState;

ASMapNode

帮助显示用户地离区域

基础使用

只需要输入要显示位置的坐标即可

ASMapNode *mapNode = [[ASMapNode alloc] init];
mapNode.style.preferredSize = CGSizeMake(300.0, 300.0);

// San Francisco
CLLocationCoordinate2D coord = CLLocationCoordinate2DMake(37.7749, -122.4194);

// show 20,000 square meters
mapNode.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000);

MKMapSnapshotOptions

可以通过option属性 定义node的主要组件部分

  • MKMapCamera: 相机的高度和仰角
  • MKMapRect:CGRect
  • MKMapRegion:控制焦点的坐标 以及 焦点周围的大小和显示
  • MKMapType:设置为Standard、Satellite(卫星)等
MKMapSnapshotOptions *options = [[MKMapSnapshotOptions alloc] init];
options.mapType = MKMapTypeSatellite;
options.region = MKCoordinateRegionMakeWithDistance(coord, 20000, 20000);
mapNode.options = options;

标记

设置标记,要做的就是为ASMapNode分配标记数组

//在地图中间直接显示图钉标记
MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
annotation.coordinate = CLLocationCoordinate2DMake(37.7749, -122.4194);

mapNode.annotations = @[annotation];

实时地图

将map node从静态的地图转换为可交互的
mapNode.liveMap = YES;

与UIKit视图一样,实时模式下的MKMapView并不是线程安全的

MKMapView Delegate

如果启用了实时地图模式,并且您需要对与地图节点关联的任何事件做出反应,则可以设置mapDelegate属性。该代理应该实现MKMapViewDelegate协议。

ASControlNode

ASControlNode与UIControl等效。我们不会直接创建实例,可以子类化其作为自己的控件。ASTextNode、ASImageNode、ASVideoNode、ASMapNode都是ASControlNode的子类

这意味着我们可以将任何文本或者图像作为按钮,而不必像UIKIt中依赖于手势识别器,或使用UIBUtton

Control state

typedef NS_OPTIONS(NSUInteger, ASControlState) {
    ASControlStateNormal       = 0,
    ASControlStateHighlighted  = 1 << 0,  // used when isHighlighted is set
    ASControlStateDisabled     = 1 << 1,
    ASControlStateSelected     = 1 << 2,  // used when isSelected is set
    ...
};

Target-Action 机制

类似UIControl ASControlNode中也有一系列事件

typedef NS_OPTIONS(NSUInteger, ASControlNodeEvent)
{
  /** A touch-down event in the control node. */
  ASControlNodeEventTouchDown         = 1 << 0,
  /** A repeated touch-down event in the control node; for this event the value of the UITouch tapCount method is greater than one. */
  ASControlNodeEventTouchDownRepeat   = 1 << 1,
  /** An event where a finger is dragged inside the bounds of the control node. */
  ASControlNodeEventTouchDragInside   = 1 << 2,
  /** An event where a finger is dragged just outside the bounds of the control. */
  ASControlNodeEventTouchDragOutside  = 1 << 3,
  /** A touch-up event in the control node where the finger is inside the bounds of the node. */
  ASControlNodeEventTouchUpInside     = 1 << 4,
  /** A touch-up event in the control node where the finger is outside the bounds of the node. */
  ASControlNodeEventTouchUpOutside    = 1 << 5,
  /** A system event canceling the current touches for the control node. */
  ASControlNodeEventTouchCancel       = 1 << 6,
  /** All events, including system events. */
  ASControlNodeEventAllEvents         = 0xFFFFFFFF
};

Hit Test Slop

尽管所有的node均有hitTestSlop属性,但是在control node中最常用

CGFloat horizontalDiff = (bounds.size.width - _playButton.bounds.size.width)/2;
CGFloat verticalDiff = (bounds.size.height - _playButton.bounds.size.height)/2;

_playButton.hitTestSlop = UIEdgeInsetsMake(-verticalDiff, -horizontalDiff, -verticalDiff, -horizontalDiff);

Hit Test Visualization

ASScrollNode

ASScrollNode是一个ASDisplayNode,其底层是一个UIScrollView.其可以自动采用ASLayoutSpec的大小作为可滚动的contentSize

自动管理contentSize

启用后,由ASSCrollNode布局规范计算的大小将定义滚动视图的.contentSize。在此模式下,滚动视图的边界会填充父级尺寸

AutomaticManagesContentSize对于实现了layoutSpecThatFits的ASScrollNode子类很有用,也适用设置了.layoutSpecBlock基类。这两种情况下都通常适用.automaticallyManagesSubnodes属性,以便将layout spec中的node自动添加到可滚动区域

适用这种方法无需捕获布局大小,使用绝对布局规范作为包装器或在代码任何位置设置contentSize,都将会将layout布局更新。通常我们将返回ASStackLayoutSpec,可滚动区域将允许查看所有内容

滚动方向

当使用automaticallyManagersContentSize时,特别因为默认是垂直而你想要水平方向时,这个属性是很有用的

这个属性控制了调整内容大小时的constrainedSize

  • Vertical:constrainedSize的.height是为无限制的(CGFLOAT_MAX)
  • Horizontal:
  • Vertical&Horizontal:

ASEditableTextNode

ASEditableTextNode通常在所有使用UITextView或者UITextField的地方使用。

在后台 使用了专门的UITextView作为其底层视图。当在主线程上操作时,你都可以直接访问和操作该视图

基础使用

如果要默认情况下有文本,则可以将属性字符串分配给attributedText属性

ASEditableTextNode *editableTextNode = [[ASEditableTextNode alloc] init];
editableTextNode.attributedText = [[NSAttributedString alloc] initWithString:@"Lorem ipsum dolor sit amet."];
editableTextNode.textContainerInset = UIEdgeInsetsMake(8, 8, 8, 8);

Placeholder Text

如果要显示占位符 只需要设置attributedPlaceholderText属性

editableTextNode.attributedPlaceholderText = [[NSAttributedString alloc] initWithString:@"Type something here..."];

属性isDisplayingPlaceholder最初为YES,当输入文本设置为非空字符串时,又将切换为NO

Typing Attributes

如果要修改用户将在在这text field中输入文字的style,可以设置typingAttributes属性

editableTextNode.typingAttributes = @{NSForegroundColorAttributeName: [UIColor blueColor], 
                                      NSBackgroundColorAttributeName: [UIColor redColor]};

ASEditableTextNode Delegate

可以使用一下delegate方法 来实现响应关联事件

//
- (void)editableTextNodeDidBeginEditing:(ASEditableTextNode *)editableTextNode;

- (BOOL)editableTextNode:(ASEditableTextNode *)editableTextNode shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;

- (void)editableTextNodeDidChangeSelection:(ASEditableTextNode *)editableTextNode fromSelectedRange:(NSRange)fromSelectedRange toSelectedRange:(NSRange)toSelectedRange dueToEditing:(BOOL)dueToEditing;

- (void)editableTextNodeDidUpdateText:(ASEditableTextNode *)editableTextNode;
- 
- (void)editableTextNodeDidFinishEditing:(ASEditableTextNode *)editableTextNode;

ASMultiplexImageNode