@@ -26,7 +26,7 @@ fileprivate final class BackingShapeLayerNode : ASDisplayNode {
2626}
2727
2828/// A node that displays shape with CAShapeLayer
29- public final class ShapeLayerNode : ASDisplayNode , @ preconcurrency ShapeDisplaying , @unchecked Sendable {
29+ public final class ShapeLayerNode : ASDisplayNode , MainActorShapeDisplaying , @unchecked Sendable {
3030
3131 private let backingNode = BackingShapeLayerNode ( )
3232
@@ -38,23 +38,54 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay
3838 }
3939 }
4040
41+ public init ( update: @escaping Update ) {
42+ self . updateClosure = update
43+ super. init ( )
44+ backgroundColor = . clear
45+ backingNode. backgroundColor = . clear
46+ backingNode. isLayerBacked = true
47+ automaticallyManagesSubnodes = true
48+ }
49+
50+ /// Warning: shape\* values will actually be set at didLoad
51+ public convenience init (
52+ update: @escaping Update ,
53+ shapeFillColor: UIColor = . clear,
54+ shapeStrokeColor: UIColor = . clear,
55+ shapeLineWidth: CGFloat = 0.0
56+ ) {
57+ self . init ( update: update)
58+ backgroundColor = . clear
59+ backingNode. backgroundColor = . clear
60+ backingNode. isLayerBacked = true
61+ automaticallyManagesSubnodes = true
62+ self . onDidLoad ( {
63+ let shapeLayerNode = ( $0 as! ShapeLayerNode )
64+ shapeLayerNode. shapeFillColor = shapeFillColor
65+ shapeLayerNode. shapeStrokeColor = shapeStrokeColor
66+ shapeLayerNode. shapeLineWidth = shapeLineWidth
67+ } )
68+ }
69+
4170 public override var supportsLayerBacking : Bool {
4271 return true
4372 }
4473
4574 @MainActor
46- public var shapeLayer : CAShapeLayer {
75+ /// Beware, direct access to lineWidth is not supported here when using usesInnerBorder, otherwise acess should be safe
76+ public var unsafeShapeLayer : CAShapeLayer {
4777 backingNode. layer
4878 }
49-
50- // To be thread-safe, using stored property
51- /// Should be set on mainThread, TODO: Discuss how to enforce the rule
79+
80+ /// cache value for bg thread access
81+ private var _shapeLineWidth : CGFloat = . zero
82+
83+ @MainActor
5284 public var shapeLineWidth : CGFloat = 0 {
5385 didSet {
54- MainActor . assumeIsolated {
55- backingNode. layer. lineWidth = shapeLineWidth
56- setNeedsLayout ( )
57- }
86+ _shapeLineWidth = shapeLineWidth
87+ backingNode. layer. lineWidth = shapeLineWidth
88+ setNeedsLayout ( )
5889 }
5990 }
6091
@@ -64,18 +95,12 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay
6495 return backingNode. layer. strokeColor. map { UIColor ( cgColor: $0) }
6596 }
6697 set {
67- // Keep it for now since we might have non confirming implementation
68- ASPerformBlockOnMainThread {
69- MainActor . assumeIsolated {
70-
71- CATransaction . begin ( )
72- CATransaction . setDisableActions ( true )
73- defer {
74- CATransaction . commit ( )
75- }
76- self . backingNode. layer. strokeColor = newValue? . cgColor
77- }
98+ CATransaction . begin ( )
99+ CATransaction . setDisableActions ( true )
100+ defer {
101+ CATransaction . commit ( )
78102 }
103+ self . backingNode. layer. strokeColor = newValue? . cgColor
79104 }
80105 }
81106
@@ -85,21 +110,15 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay
85110 return backingNode. layer. fillColor. map { UIColor ( cgColor: $0) }
86111 }
87112 set {
88- // Keep it for now since we might have non confirming implementation
89- ASPerformBlockOnMainThread {
90- MainActor . assumeIsolated {
91-
92- CATransaction . begin ( )
93- CATransaction . setDisableActions ( true )
94- defer {
95- CATransaction . commit ( )
96- }
97- self . backingNode. layer. fillColor = newValue? . cgColor
98- }
113+ CATransaction . begin ( )
114+ CATransaction . setDisableActions ( true )
115+ defer {
116+ CATransaction . commit ( )
99117 }
118+ self . backingNode. layer. fillColor = newValue? . cgColor
100119 }
101120 }
102-
121+
103122 public override func layout( ) {
104123 super. layout ( )
105124 CATransaction . begin ( )
@@ -125,27 +144,16 @@ public final class ShapeLayerNode : ASDisplayNode, @preconcurrency ShapeDisplay
125144 }
126145 }
127146
128- public init (
129- update: @escaping Update
130- ) {
131- self . updateClosure = update
132- super. init ( )
133- backgroundColor = . clear
134- backingNode. backgroundColor = . clear
135- backingNode. isLayerBacked = true
136- automaticallyManagesSubnodes = true
137- }
138-
139147 public override func layoutSpecThatFits( _ constrainedSize: ASSizeRange ) -> ASLayoutSpec {
140148
141149 if usesInnerBorder {
142150 return ASWrapperLayoutSpec (
143151 layoutElement: ASInsetLayoutSpec (
144152 insets: . init(
145- top: shapeLineWidth / 2 ,
146- left: shapeLineWidth / 2 ,
147- bottom: shapeLineWidth / 2 ,
148- right: shapeLineWidth / 2
153+ top: _shapeLineWidth / 2 ,
154+ left: _shapeLineWidth / 2 ,
155+ bottom: _shapeLineWidth / 2 ,
156+ right: _shapeLineWidth / 2
149157 ) ,
150158 child: backingNode
151159 )
0 commit comments