In our present Swift Discuss sequence
, we’ve got been reimplenting elements of SwiftUI’s structure system to achieve a deeper understanding of the way it works. Final week we examined SwiftUI’s grid views and reimplemented their structure algorithm. Some elements of the conduct actually stunned us.
A couple of days in the past we tweeted a sequence of structure quizzes
for SwiftUI’s LazyVGrid
to spotlight a few of the much less apparent behaviors. On this submit we’ll check out all three quiz questions and clarify why the grid lays out its contents in the best way it does.
Apparently, we weren’t the one ones struggling to grasp the conduct of grids: none of the preferred quiz solutions have been appropriate!
#1: Fastened and Adaptive Columns
The primary instance defines a grid with a set and an adaptive column. The grid has a set width of 200 factors:
LazyVGrid(columns: [
GridItem(.fixed(70)),
GridItem(.adaptive(minimum: 40))
]) {
}
.body(width: 200)
.border(Colour.white)
The three options we requested you to select from:
Answer A is appropriate.
For grids, fastened measurement columns are at all times rendered precisely on the specified width, irrespective of how a lot area is offered. Subsequently, the primary column renders as precisely 70 factors huge.
The grid subtracts the fastened column widths and the spacing between columns (which is the default spacing of 8 factors) from the proposed width (which is 200 factors on this instance), leaving us with a remaining width of 122 factors. This width is then distributed to the remaining columns. On this case there’s just one column left, an adaptive column, so it takes up the remaining width of 122 factors.
Adaptive columns are particular: SwiftUI tries to suit as many grid gadgets as doable into an adaptive column; it divides the column width by the minimal width, taking spacing into consideration.
In our instance above, we’ve got a default spacing of 8 factors, and we are able to match two columns (40 + 8 + 40) into the 122 factors. Making an attempt to suit three columns (40 + 8 + 40 + 8 + 40) fails. The adaptive column has an efficient width of 122 minus 8, giving 114 factors to distribute. Dividing 114 factors by the variety of columns offers us two gadgets which are 57 huge.
#2: Versatile and Adaptive Columns
The second quiz has a grid with a versatile and an adaptive column:
LazyVGrid(columns: [
GridItem(.flexible(minimum: 140)),
GridItem(.adaptive(minimum: 70))
], content material: {
})
.body(width: 200)
.border(Colour.white)
The three potential options:
Answer C is appropriate.
Not like the primary instance, this grid would not have any fastened columns, so the grid begins out with an out there width of 200 factors minus 8 factors of default spacing between the 2 columns, which provides us 192 factors.
Then the grid loops over the remaining (nonfixed) columns so as, and calculates every column width as remaining width divided by the variety of remaining columns, clamped by the minimal and most constraints on the columns.
On this instance, the width of the primary column is calculated as 192 factors of remaining width divided by 2 remaining columns, which equals 96 factors. Since we specified a minimal width of 140 factors for the primary, versatile column, 96 factors will get clamped to the vary between 140 and infinity. The column turns into its minimal width of 140 factors, and the remaining width is now 192 minus 140, giving 52 factors.
The width of the second column is now calculated as 52 factors remaining width divided by 1 remaining column, which equals 52 factors. We would anticipate this outcome to be clamped to the adaptive column’s minimal width of 70 factors, however the minimal property of an adaptive column is just used to compute the variety of gadgets inside that column. The adaptive column thus turns into 52 factors huge.
The merchandise within the adaptive column is being rendered with a width of 52 factors as properly, though we have specified a minimal of 70 factors. If there’s much less area out there than the minimal merchandise width, the minimal width is ignored and the merchandise will get rendered at no matter width is left over.
#3: A number of Versatile Columns
The third quiz has a grid with two versatile columns:
LazyVGrid(columns: [
GridItem(.flexible(minimum: 50)),
GridItem(.flexible(minimum: 120))
], content material: {
})
.body(width: 200)
.border(Colour.white)
The three potential options:
Answer A is appropriate.
This seemingly easy structure exhibits maybe essentially the most complicated conduct of the three examples on this submit. Not solely does the grid render out of bounds — though the columns’ minimums would fortunately match into the out there width of 200 factors — it additionally renders out of heart of its enclosing 200pointswide body.
Let’s undergo the steps of the grid’s structure algorithm and see what’s occurring. We begin once more with a remaining width of 200 factors minus 8 factors of default spacing, which provides us 192 factors. For the primary column, we calculate the width as 192 divided by 2 remaining columns, which equals 96 factors. Because the first column has a minimal width of fifty factors, the width of 96 factors is not affected by the clamping, so the remaining width stands at 96 factors. The second column turns into 96 factors clamped to its minimal of 120 factors, i.e. 120 factors huge.
Nevertheless, that is not what we see within the rendering of this grid: the primary column renders 108 factors huge, the second renders 120 factors huge, whereas we calculated 96 factors and 120 factors.
To know this half we’ve got to keep in mind that SwiftUI first calculates the frames of all views, earlier than it renders the views in a second go. With our calculation above, the general width of the grid is calculated as 96 + 8 + 120 = 224 factors. The fastened body with a width of 200 factors across the grid then facilities the grid, shifting it (224200)/2 = 12 factors to the left.
When it is time for the grid to render itself, it begins out with the width decided within the structure go, which is 224 factors, however to truly render it calculates the column widths once more
primarily based on the width of 224 factors!
On this regard, grids differ considerably from stacks: stacks will bear in mind the sizes of their youngsters between structure and rendering, and subsequently keep away from this surprising conduct, whereas grids do not appear to try this.
Let’s undergo the column width calculations as soon as once more, beginning out with a remaining width of 224 factors minus 8 factors spacing, or 216 factors. The primary column turns into 216 factors divided by 2 remaining columns, equalling 108 factors. The remaining width is now 216 minus 108, giving us 108 factors. The second column turns into 108 factors clamped to its miminum of 120 factors.
Et voilà! We have arrived on the appropriate column widths of 108 and 120 factors.
Because the body across the grid has calculated the grid’s origin primarily based on the unique width of 224 factors, however the grid now renders itself with an total width of 108+8+120 = 236 factors, the grid seems out of heart by 6 factors.
Conclusion
In abstract, these are the steps the grid’s structure algorithm takes:

Begin out with the proposed width because the remaining width.

Subtract the width of all fastened width columns, in addition to the spacing between columns.

Iterate over the remaining columns so as and

calculate every column’s width as remaining width divided by the variety of remaining columns, clamped to the column’s minimal and most.

subtract the column’s width from the remaining width.

Understand that this algorithm runs as soon as throughout structure, after which once more throughout rendering. Throughout structure the algorithm begins with the proposed width of the dad or mum view, whereas throughout rendering it begins with the calculated total width from the preliminary structure go.
This conduct may be fairly unintuitive, however we hope that this submit will show you how to perceive that behaviour higher, and obtain the outcomes you intention for.
We’ll be persevering with the SwiftUI Structure Defined
sequence via December. Every episode reimplements a side of the structure system, and with six episodes already launched there’s a lot to be taught!
To help us, and entry our whole catalogue, subscribe right here
.