Texture 浅析

文章目录
  1. 1. Nodes
  2. 2. 子类化
    1. 2.1. 自定义 ASDisplayNode 子类
    2. 2.2. 自定义 ASViewController 子类
  3. 3. 布局
  4. 4. Node 是如何绘制的

Nodes

ASDisplayNodeUIView 的抽象层, 就像 UIViewCALayer 的抽象层一样. 不同的是 UIKit 只能运行在主线程,nodes 是线程安全的,可以在后台线程上并行实例化和配置它们的整个层次结构。

texture_node

子类化

自定义 ASDisplayNode 子类

如果想使用 Texture,需要自定义类继承自 ASDisplayNode 需要注意方法:

注意:UIKit 相关属性方法只能在 main thread 中使用,注意 ASDisplayNode 方法在 main/background thread 中实现很重要

ASDisplayNode方法线程功能描述
initbackground这个方法很少用(因为background),使用 nodeBlocks 的时调用,初始化方法调用才可以调用其他方法
didLoadmain类似 viewDidLoad,添加 UIKit objects初始化,配置objects 属性,事件……
layoutSpecThatFitsbackground布局代码(返回 layout spec object布局nodes),layout spec object 对象不会cache,而是在合适(啥时候?)的时候调用重新创建
layoutmain调用 super.layout 方法会调用layoutSpecThatFits,这样所有的 nodes 都会布局完成,类似 viewWillLayoutSubviews 方法,很少使用,如果想这样subnode.frame = self.bounds 那就用吧

自定义 ASViewController 子类

ASViewControllerUIViewController 的子类,所以 ASViewController 需要在 main thread 中使用。

ASViewController 管理 node 就像 UIViewController 管理 view 一样
但是 ASViewController 的指派构造函数与 UIViewController 不同

1
2
3
4
5
6
7
init() {
let pagerNode = ASPagerNode()
super.init(node: pagerNode)

pagerNode.setDataSource(self)
pagerNode.setDelegate(self)
}

-loadView
在 loadView 如果你不改变 self.view 的值,就可以,不过建议你使用 viewDidLoad 方法. 调用 [super loadView] 会设置 node.view .

-viewDidLoad
ASViewController 的生命周期中,这个方法只会,在 loadView 调用以后立即调用一次。那些只会调用一次的配置放在这里最好。

布局代码不应该放在这里,因为几何元素改变这个方法不会调用(当然可以在viewDidLoad中配置监听者模式监听)

-viewWillLayoutSubviews
该方法与 node-layout 方法在同一时间被调用,并且在ASViewController的生命周期中可能被多次调用。
只要 ASViewController 的 node 的 bounds 改变(包括旋转,分屏,键盘显示),以及层次结构发生了更改(添加,删除或更改了 children),它就会被调用
为了保持一致性,最佳做法是将所有布局代码放入此方法中。因为它的调用频率不高,所以不直接依赖于大小的代码也属于此处。

-viewWillAppear: / -viewDidDisappear:
这些方法在ASViewController的node出现在屏幕上之前(最早可见)和刚从视图层次结构中删除之后(不再可见的最早时间)被调用。这些方法提供了一个很好的机会来启动或停止与控制器的演示或解雇有关的动画。这也是记录用户操作的好地方。

尽管可以多次调用这些方法并且可以获取几何图形信息,但是并不能为所有几何图形更改调用它们,因此,不应将它们用于核心布局代码(超出特定动画所需的设置)。

布局

Node 是如何绘制的

下面是 一个 swift 版本的 YYAyncLayer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
public class AsyncLayer: CALayer {
// 1. 多串行队列控制thread并发数
private static let queues: [DispatchQueue] = {
(0 ... $0).map { _ in DispatchQueue(label: "com.lee.async.render") }
} (max(min(ProcessInfo().activeProcessorCount, 16), 1))

private static var current = 0
// 拿到指定的 queue 分配任务
private static var display: DispatchQueue {
objc_sync_enter(self)
current += current == Int.max ? -current : 1
objc_sync_exit(self)
return queues[current % queues.count]
}
private var sentinel = 0

/// 是否异步处理
public var isAsynchronously: Bool = true

public override class func defaultValue(forKey key: String) -> Any? {
guard key == "isAsynchronously" else {
return super.defaultValue(forKey: key)
}
return true
}

// 如果通知重新渲染了就取消,就取消当前渲染
public override func setNeedsDisplay() {
cancel()
super.setNeedsDisplay()
}

public override func display() {
super.contents = super.contents
display(isAsynchronously)
}

public func cancel() {
objc_sync_enter(self)
sentinel += 1
objc_sync_exit(self)
}

deinit { cancel() }
}

extension AsyncLayer {
private func display(_ async: Bool) {
guard let delegate = delegate as? AsyncLayerDelegate else { return }

delegate.display(will: self)
if async {
let size = bounds.size
let opaque = isOpaque
let scale = UIScreen.main.scale
let background = (opaque && (backgroundColor != nil)) ? backgroundColor : nil
// 使用闭包捕获当前值,跟 self.sentinel 比较,如果self.sentinel 的值变了,说明渲染取消
let current = sentinel
let isCancelled = { return current != self.sentinel }

AsyncLayer.display.async { [weak self] in
guard let self = self else { return }
guard !isCancelled() else { return }

UIGraphicsBeginImageContextWithOptions(size, opaque, scale)
guard let context = UIGraphicsGetCurrentContext() else { return }

if opaque {// 不透明
context.saveGState()
let rect = CGRect(x: 0, y: 0, width: size.width * scale, height: size.height * scale)
if background == nil || background?.alpha != 1 {
context.setFillColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1))
context.addRect(rect)
context.fillPath()
}
if let color = background {
context.setFillColor(color)
context.addRect(rect)
context.fillPath()
}
context.restoreGState()
}

delegate.display(draw: self, at: context, with: size, isCancelled: isCancelled)

if isCancelled() {
UIGraphicsEndImageContext()
// 主线程会回调渲染
DispatchQueue.main.async {
delegate.display(did: self, with: false)
}
return
}

let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

if isCancelled() {
UIGraphicsEndImageContext()
DispatchQueue.main.async {
delegate.display(did: self, with: false)
}
return
}

DispatchQueue.main.async {
if isCancelled() {
delegate.display(did: self, with: false)
} else {
self.contents = image?.cgImage
delegate.display(did: self, with: true)
}
}
}
} else {
UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, UIScreen.main.scale)
guard let context = UIGraphicsGetCurrentContext() else { return }

if isOpaque {
var size = bounds.size
size.width *= contentsScale
size.height *= contentsScale
context.saveGState()

if backgroundColor == nil || backgroundColor!.alpha < 1 {
context.setFillColor(UIColor.white.cgColor)
context.addRect(CGRect(origin: .zero, size: size))
context.fillPath()
}
if let color = backgroundColor {
context.setFillColor(color)
context.addRect(CGRect(origin: .zero, size: size))
context.fillPath()
}
context.restoreGState()
}

delegate.display(draw: self, at: context, with: bounds.size) { false }

let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
contents = image?.cgImage
delegate.display(did: self, with: true)
}
}
}

触发绘画的事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

struct Transaction {}
extension Transaction {
static func commit(_ target: AnyObject, with selector: Selector) {
let item = Item(target, with: selector)
Transaction.configObserver
waits.insert(item)
}
}

extension Transaction {
// transaction item 集合,确保插入事务唯一
private static var waits: Set<Item> = []

private static let configObserver: Void = {
let runloop = CFRunLoopGetCurrent()
// observer 通知什么时候渲染
let observer = CFRunLoopObserverCreate(
kCFAllocatorDefault,
CFRunLoopActivity.beforeWaiting.rawValue | CFRunLoopActivity.exit.rawValue,
true, // repeat
0xFFFFFF, // after CATransaction(2000000)
callback,
nil
)
CFRunLoopAddObserver(runloop, observer, .commonModes)
} ()
private static let callback: CFRunLoopObserverCallBack = { _,_,_ in
waits.forEach { _ = $0.target.perform($0.selector) }
waits = []
}
}

extension Transaction {
private struct Item: Hashable {
let target: AnyObject
let selector: Selector

init(_ target: AnyObject, with selector: Selector) {
self.target = target
self.selector = selector
}

func hash(into hasher: inout Hasher) {
hasher.combine(target.hash)
hasher.combine(selector)
}

static func == (lhs: Transaction.Item, rhs: Transaction.Item) -> Bool {
return lhs.target === rhs.target && lhs.selector == rhs.selector
}
}
}

引用:

Texture