@@ -27,29 +27,50 @@ public enum LayoutSystem {
2727 }
2828 }
2929
30+ /// - Parameter inheritStackLayoutParticipation: If `true`, the stack layout
31+ /// will have ``ViewSize/participateInStackLayoutsWhenEmpty`` set to `true`
32+ /// if all of its children have it set to true. This allows views such as
33+ /// ``Group`` to avoid changing stack layout participation (since ``Group``
34+ /// is meant to appear completely invisible to the layout system).
3035 public static func updateStackLayout< Backend: AppBackend > (
3136 container: Backend . Widget ,
3237 children: [ LayoutableChild ] ,
3338 proposedSize: SIMD2 < Int > ,
3439 environment: Environment ,
3540 backend: Backend ,
36- dryRun: Bool
41+ dryRun: Bool ,
42+ inheritStackLayoutParticipation: Bool = false
3743 ) -> ViewSize {
3844 let spacing = environment. layoutSpacing
3945 let alignment = environment. layoutAlignment
4046 let orientation = environment. layoutOrientation
41- let totalSpacing = ( children. count - 1 ) * spacing
42-
43- var spaceUsedAlongStackAxis = 0
44- var childrenRemaining = children. count
4547
4648 var renderedChildren = [ ViewSize] (
4749 repeating: . empty,
4850 count: children. count
4951 )
5052
51- // My thanks go to this great article for investigating and explaining how SwiftUI determines
52- // child view 'flexibility': https://www.objc.io/blog/2020/11/10/hstacks-child-ordering/
53+ // Figure out which views to treat as hidden. This could be the cause
54+ // of issues if a view has some threshold at which it suddenly becomes
55+ // invisible.
56+ var isHidden = [ Bool] ( repeating: false , count: children. count)
57+ for (i, child) in children. enumerated ( ) {
58+ let size = child. computeSize (
59+ proposedSize: proposedSize,
60+ environment: environment
61+ )
62+ isHidden [ i] =
63+ size. size == . zero
64+ && !size. participateInStackLayoutsWhenEmpty
65+ }
66+
67+ // My thanks go to this great article for investigating and explaining
68+ // how SwiftUI determines child view 'flexibility':
69+ // https://www.objc.io/blog/2020/11/10/hstacks-child-ordering/
70+ let visibleChildrenCount = isHidden. count { hidden in
71+ !hidden
72+ }
73+ let totalSpacing = ( visibleChildrenCount - 1 ) * spacing
5374 let proposedSizeWithoutSpacing = SIMD2 (
5475 proposedSize. x - ( orientation == . horizontal ? totalSpacing : 0 ) ,
5576 proposedSize. y - ( orientation == . vertical ? totalSpacing : 0 )
@@ -66,11 +87,40 @@ public enum LayoutSystem {
6687 size. maximumHeight - Double( size. minimumHeight)
6788 }
6889 }
69- let sortedChildren = zip ( children. enumerated ( ) , flexibilities) . sorted { first, second in
70- first. 1 <= second. 1
71- } . map ( \. 0 )
90+ let sortedChildren = zip ( children. enumerated ( ) , flexibilities)
91+ . sorted { first, second in
92+ first. 1 <= second. 1
93+ }
94+ . map ( \. 0 )
7295
96+ var spaceUsedAlongStackAxis = 0
97+ var childrenRemaining = visibleChildrenCount
7398 for (index, child) in sortedChildren {
99+ // No need to render visible children.
100+ if isHidden [ index] {
101+ // Update child in case it has just changed from visible to hidden,
102+ // and to make sure that the view is still hidden (if it's not then
103+ // it's a bug with either the view or the layout system).
104+ let size = child. update (
105+ proposedSize: . zero,
106+ environment: environment,
107+ dryRun: dryRun
108+ )
109+ if size. size != . zero || size. participateInStackLayoutsWhenEmpty {
110+ print ( " warning: Hidden view became visible on second update. Layout may break. " )
111+ }
112+ renderedChildren [ index] = ViewSize (
113+ size: . zero,
114+ idealSize: . zero,
115+ minimumWidth: 0 ,
116+ minimumHeight: 0 ,
117+ maximumWidth: 0 ,
118+ maximumHeight: 0 ,
119+ participateInStackLayoutsWhenEmpty: false
120+ )
121+ continue
122+ }
123+
74124 let proposedWidth : Double
75125 let proposedHeight : Double
76126 switch orientation {
@@ -149,6 +199,13 @@ public enum LayoutSystem {
149199 var x = 0
150200 var y = 0
151201 for (index, childSize) in renderedChildren. enumerated ( ) {
202+ // Avoid the whole iteration if the child is hidden. If there
203+ // are weird positioning issues for views that do strange things
204+ // then this could be the cause.
205+ if isHidden [ index] {
206+ continue
207+ }
208+
152209 // Compute alignment
153210 switch ( orientation, alignment) {
154211 case ( . vertical, . leading) :
@@ -182,7 +239,10 @@ public enum LayoutSystem {
182239 minimumWidth: minimumWidth,
183240 minimumHeight: minimumHeight,
184241 maximumWidth: maximumWidth,
185- maximumHeight: maximumHeight
242+ maximumHeight: maximumHeight,
243+ participateInStackLayoutsWhenEmpty:
244+ inheritStackLayoutParticipation
245+ && isHidden. allSatisfy { $0 }
186246 )
187247 }
188248}
0 commit comments