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
可以和ASTableNodes
、ASCollectionNodes
、ASPageNodes
一起使用
使用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
为了暂时解决此问题 可以通过设置truncationMode
为NSLineBreakByTruncatingTail
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的大小
- style.preferredSize
当我们想要使用标准尺寸时
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
{
imageNode.style.preferredSize = CGSizeMake(100, 200);
...
return finalLayoutSpec;
}
- ASRatioLayoutSpec
使用ASRatioLayoutSpec
为图像分配一个比例,当图像加载完成后 将会保持该比例
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constraint
{
CGFloat ratio = 3.0/1.0;
ASRatioLayoutSpec *imageRatioSpec = [ASRatioLayoutSpec ratioLayoutSpecWithRatio:ratio child:self.imageNode];
...
return finalLayoutSpec;
}
引擎组件
如果选择没有包括PINRemoteImage
和PINCache
,那么将失去对jpeg
的渐进支持,并且需要自定义实现ASImageCacheProtocol
的缓存
渐进式JPEG支持
由于包含了PINRemoteImage
,NetworkImageNode现在支持加载渐进式的JPEG。意味着,如果服务器提供了这种jpeg,则图像会将以较低的质量快速显示,并且随着加载更多的数据而扩大
启用渐进加载只需要设置shouldRenderProgressImages
为YES
networkImageNode.shouldRenderProgressImages = YES;
注意
这种方式是使用一张逐渐加载的图像。如果服务器只能使用常规的JPEG,但是可以提供多个版本来提高质量,那么应该使用
ASMultiplexImageNode
自动缓存
使用PINCache
自动缓存网络图像
GIF支持
ASNetworkImageNode通过PINRemoteImage的beta版PINAnimatedImage提供GIF支持。注意!除非shouldCacheImage设置为NO,否则该支持将不适用于本地文件。
ASVideoNode
方便我们在app中展示视频
使用
ASVideoNode
就会连接AVFoundation
,因为在内部底层中使用了AVPlayerLayer
和AVFoundataion
基础使用
最简单的方式就是设置一个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;