-
Notifications
You must be signed in to change notification settings - Fork 27
Expand file tree
/
Copy pathHighlightCellNode.swift
More file actions
154 lines (124 loc) · 3.92 KB
/
HighlightCellNode.swift
File metadata and controls
154 lines (124 loc) · 3.92 KB
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
147
148
149
150
151
152
153
154
import AsyncDisplayKit
import Descriptors
/**
Provides highlighting function without tap handling for CollectionView.
*/
open class HighlightCellNode<D: ASDisplayNode>: NamedDisplayCellNodeBase {
public typealias BodyNode = D
open override var supportsLayerBacking: Bool {
return false
}
public private(set) var overlayNode: ASDisplayNode?
public let bodyNode: D
private let animationTargetNode: ASDisplayNode
private let highlightAnimation: HighlightAnimationDescriptor
private let haptics: HapticsDescriptor?
private var highlightHandler: HighlightAnimationDescriptor.Context.Handler?
private var onTapClosure: @MainActor () -> Void = {}
private var onChangedSelectedClosure: (Bool) -> Void = { _ in }
// MARK: - Initializers
public init(
animation: HighlightAnimationDescriptor,
haptics: HapticsDescriptor? = nil,
contentNode: D
) {
self.haptics = haptics
let body = contentNode
self.bodyNode = body
if body.isLayerBacked {
/**
If body uses layer-backing, it can't take a view to animate by UIView based animation.
With wrapping another node, it enables body-node can be animatable.
*/
self.animationTargetNode = AnyWrapperNode(content: body)
} else {
self.animationTargetNode = body
}
self.highlightAnimation = animation
super.init()
clipsToBounds = false
addSubnode(animationTargetNode)
}
@available(*, deprecated, message: "Use contentNode parameter for bodyNode")
public init(
animation: HighlightAnimationDescriptor,
haptics: HapticsDescriptor? = nil,
_ bodyNode: () -> D
) {
self.haptics = haptics
let body = bodyNode()
self.bodyNode = body
if body.isLayerBacked {
/**
If body uses layer-backing, it can't take a view to animate by UIView based animation.
With wrapping another node, it enables body-node can be animatable.
*/
self.animationTargetNode = AnyWrapperNode { body }
} else {
self.animationTargetNode = body
}
self.highlightAnimation = animation
super.init()
clipsToBounds = false
addSubnode(animationTargetNode)
}
open override func didLoad() {
super.didLoad()
let context = highlightAnimation.prepare()
if let overlay = context.overlay {
let node = ASDisplayNode.init(viewBlock: {
overlay
})
self.overlayNode = node
addSubnode(node)
setNeedsLayout()
}
self.highlightHandler = context.handler
}
open override func layoutSpecThatFits(
_ constrainedSize: ASSizeRange
) -> ASLayoutSpec {
if let overlayNode = self.overlayNode {
return ASOverlayLayoutSpec(child: animationTargetNode, overlay: overlayNode)
} else {
return ASWrapperLayoutSpec(layoutElement: animationTargetNode)
}
}
open override var isHighlighted: Bool {
didSet {
highlightHandler?(isHighlighted, self.view, animationTargetNode.view)
}
}
open override var isSelected: Bool {
didSet {
guard isSelected != oldValue else {
return
}
onChangedSelectedClosure(isSelected)
}
}
@MainActor
open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesBegan(touches, with: event)
haptics?.send(event: .onTouchDownInside)
}
@MainActor
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
super.touchesEnded(touches, with: event)
haptics?.send(event: .onTouchUpInside)
onTapClosure()
}
@discardableResult
public func onTap(_ handler: @escaping @MainActor () -> Void) -> Self {
onTapClosure = handler
return self
}
@discardableResult
public func onChangedIsSelected(_ handler: @escaping (Bool) -> Void) -> Self {
onChangedSelectedClosure = handler
return self
}
}
/// To avoid runtime error with ObjC
open class AnyHighlightCellNode: HighlightCellNode<ASDisplayNode> {
}