@@ -45,28 +45,34 @@ public enum LayoutSystem {
4545 }
4646
4747 public struct LayoutableChild {
48- private var update :
48+ private var computeLayout :
4949 (
5050 _ proposedSize: SIMD2 < Int > ,
51- _ environment: EnvironmentValues ,
52- _ dryRun : Bool
53- ) -> ViewUpdateResult
51+ _ environment: EnvironmentValues
52+ ) -> ViewLayoutResult
53+ private var _commit : ( ) -> ViewLayoutResult
5454 var tag : String ?
5555
5656 public init (
57- update: @escaping ( SIMD2 < Int > , EnvironmentValues , Bool ) -> ViewUpdateResult ,
57+ computeLayout: @escaping ( SIMD2 < Int > , EnvironmentValues ) -> ViewLayoutResult ,
58+ commit: @escaping ( ) -> ViewLayoutResult ,
5859 tag: String ? = nil
5960 ) {
60- self . update = update
61+ self . computeLayout = computeLayout
62+ self . _commit = commit
6163 self . tag = tag
6264 }
6365
64- public func update (
66+ public func computeLayout (
6567 proposedSize: SIMD2 < Int > ,
6668 environment: EnvironmentValues ,
6769 dryRun: Bool = false
68- ) -> ViewUpdateResult {
69- update ( proposedSize, environment, dryRun)
70+ ) -> ViewLayoutResult {
71+ computeLayout ( proposedSize, environment)
72+ }
73+
74+ public func commit( ) -> ViewLayoutResult {
75+ _commit ( )
7076 }
7177 }
7278
@@ -75,61 +81,43 @@ public enum LayoutSystem {
7581 /// if all of its children have it set to true. This allows views such as
7682 /// ``Group`` to avoid changing stack layout participation (since ``Group``
7783 /// is meant to appear completely invisible to the layout system).
78- public static func updateStackLayout < Backend: AppBackend > (
84+ public static func computeStackLayout < Backend: AppBackend > (
7985 container: Backend . Widget ,
8086 children: [ LayoutableChild ] ,
8187 proposedSize: SIMD2 < Int > ,
8288 environment: EnvironmentValues ,
8389 backend: Backend ,
84- dryRun: Bool ,
8590 inheritStackLayoutParticipation: Bool = false
86- ) -> ViewUpdateResult {
91+ ) -> ViewLayoutResult {
8792 let spacing = environment. layoutSpacing
88- let alignment = environment. layoutAlignment
8993 let orientation = environment. layoutOrientation
9094
91- var renderedChildren : [ ViewUpdateResult ] = Array (
92- repeating: ViewUpdateResult . leafView ( size: . empty) ,
95+ var renderedChildren : [ ViewLayoutResult ] = Array (
96+ repeating: ViewLayoutResult . leafView ( size: . empty) ,
9397 count: children. count
9498 )
9599
96- // Figure out which views to treat as hidden. This could be the cause
97- // of issues if a view has some threshold at which it suddenly becomes
98- // invisible.
100+ // My thanks go to this great article for investigating and explaining
101+ // how SwiftUI determines child view 'flexibility':
102+ // https://www.objc.io/blog/2020/11/10/hstacks-child-ordering/
99103 var isHidden = [ Bool] ( repeating: false , count: children. count)
100- for (i , child ) in children. enumerated ( ) {
101- let result = child. update (
104+ let flexibilities = children. enumerated ( ) . map { i , child in
105+ let result = child. computeLayout (
102106 proposedSize: proposedSize,
103- environment: environment,
104- dryRun: true
107+ environment: environment
105108 )
106109 isHidden [ i] = !result. participatesInStackLayouts
107- }
108-
109- // My thanks go to this great article for investigating and explaining
110- // how SwiftUI determines child view 'flexibility':
111- // https://www.objc.io/blog/2020/11/10/hstacks-child-ordering/
112- let visibleChildrenCount = isHidden. filter { hidden in
113- !hidden
114- } . count
115- let totalSpacing = max ( visibleChildrenCount - 1 , 0 ) * spacing
116- let proposedSizeWithoutSpacing = SIMD2 (
117- proposedSize. x - ( orientation == . horizontal ? totalSpacing : 0 ) ,
118- proposedSize. y - ( orientation == . vertical ? totalSpacing : 0 )
119- )
120- let flexibilities = children. map { child in
121- let size = child. update (
122- proposedSize: proposedSizeWithoutSpacing,
123- environment: environment,
124- dryRun: true
125- ) . size
126110 return switch orientation {
127111 case . horizontal:
128- size. maximumWidth - Double( size. minimumWidth)
112+ result . size. maximumWidth - Double( result . size. minimumWidth)
129113 case . vertical:
130- size. maximumHeight - Double( size. minimumHeight)
114+ result . size. maximumHeight - Double( result . size. minimumHeight)
131115 }
132116 }
117+ let visibleChildrenCount = isHidden. filter { hidden in
118+ !hidden
119+ } . count
120+ let totalSpacing = max ( visibleChildrenCount - 1 , 0 ) * spacing
133121 let sortedChildren = zip ( children. enumerated ( ) , flexibilities)
134122 . sorted { first, second in
135123 first. 1 <= second. 1
@@ -144,10 +132,9 @@ public enum LayoutSystem {
144132 // Update child in case it has just changed from visible to hidden,
145133 // and to make sure that the view is still hidden (if it's not then
146134 // it's a bug with either the view or the layout system).
147- let result = child. update (
135+ let result = child. computeLayout (
148136 proposedSize: . zero,
149- environment: environment,
150- dryRun: dryRun
137+ environment: environment
151138 )
152139 if result. participatesInStackLayouts {
153140 print (
@@ -177,13 +164,12 @@ public enum LayoutSystem {
177164 proposedWidth = Double ( proposedSize. x)
178165 }
179166
180- let childResult = child. update (
167+ let childResult = child. computeLayout (
181168 proposedSize: SIMD2 < Int > (
182169 Int ( proposedWidth. rounded ( . towardZero) ) ,
183170 Int ( proposedHeight. rounded ( . towardZero) )
184171 ) ,
185- environment: environment,
186- dryRun: dryRun
172+ environment: environment
187173 )
188174
189175 renderedChildren [ index] = childResult
@@ -247,53 +233,13 @@ public enum LayoutSystem {
247233 + totalSpacing
248234 }
249235
250- if !dryRun {
251- backend. setSize ( of: container, to: size)
252-
253- var x = 0
254- var y = 0
255- for (index, childSize) in renderedChildren. enumerated ( ) {
256- // Avoid the whole iteration if the child is hidden. If there
257- // are weird positioning issues for views that do strange things
258- // then this could be the cause.
259- if isHidden [ index] {
260- continue
261- }
262-
263- // Compute alignment
264- switch ( orientation, alignment) {
265- case ( . vertical, . leading) :
266- x = 0
267- case ( . horizontal, . leading) :
268- y = 0
269- case ( . vertical, . center) :
270- x = ( size. x - childSize. size. size. x) / 2
271- case ( . horizontal, . center) :
272- y = ( size. y - childSize. size. size. y) / 2
273- case ( . vertical, . trailing) :
274- x = ( size. x - childSize. size. size. x)
275- case ( . horizontal, . trailing) :
276- y = ( size. y - childSize. size. size. y)
277- }
278-
279- backend. setPosition ( ofChildAt: index, in: container, to: SIMD2 < Int > ( x, y) )
280-
281- switch orientation {
282- case . horizontal:
283- x += childSize. size. size. x + spacing
284- case . vertical:
285- y += childSize. size. size. y + spacing
286- }
287- }
288- }
289-
290236 // If the stack has been told to inherit its stack layout participation
291237 // and all of its children are hidden, then the stack itself also
292238 // shouldn't participate in stack layouts.
293239 let shouldGetIgnoredInStackLayouts =
294240 inheritStackLayoutParticipation && isHidden. allSatisfy { $0 }
295241
296- return ViewUpdateResult (
242+ return ViewLayoutResult (
297243 size: ViewSize (
298244 size: size,
299245 idealSize: idealSize,
@@ -308,4 +254,57 @@ public enum LayoutSystem {
308254 childResults: renderedChildren
309255 )
310256 }
257+
258+ public static func commitStackLayout< Backend: AppBackend > (
259+ container: Backend . Widget ,
260+ children: [ LayoutableChild ] ,
261+ layout: ViewLayoutResult ,
262+ environment: EnvironmentValues ,
263+ backend: Backend
264+ ) {
265+ let size = layout. size. size
266+ backend. setSize ( of: container, to: size)
267+
268+ let renderedChildren = children. map { $0. commit ( ) }
269+
270+ let alignment = environment. layoutAlignment
271+ let spacing = environment. layoutSpacing
272+ let orientation = environment. layoutOrientation
273+
274+ var x = 0
275+ var y = 0
276+ for (index, child) in renderedChildren. enumerated ( ) {
277+ // Avoid the whole iteration if the child is hidden. If there
278+ // are weird positioning issues for views that do strange things
279+ // then this could be the cause.
280+ if !child. participatesInStackLayouts {
281+ continue
282+ }
283+
284+ // Compute alignment
285+ switch ( orientation, alignment) {
286+ case ( . vertical, . leading) :
287+ x = 0
288+ case ( . horizontal, . leading) :
289+ y = 0
290+ case ( . vertical, . center) :
291+ x = ( size. x - child. size. size. x) / 2
292+ case ( . horizontal, . center) :
293+ y = ( size. y - child. size. size. y) / 2
294+ case ( . vertical, . trailing) :
295+ x = ( size. x - child. size. size. x)
296+ case ( . horizontal, . trailing) :
297+ y = ( size. y - child. size. size. y)
298+ }
299+
300+ backend. setPosition ( ofChildAt: index, in: container, to: SIMD2 < Int > ( x, y) )
301+
302+ switch orientation {
303+ case . horizontal:
304+ x += child. size. size. x + spacing
305+ case . vertical:
306+ y += child. size. size. y + spacing
307+ }
308+ }
309+ }
311310}
0 commit comments