SwiftUI’s Grid Views · objc.io

0
193


In our present Swift Discuss collection
, we’ve got been re-implenting 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 re-implemented their structure algorithm. Some elements of the conduct actually stunned us.

Just a few days in the past we tweeted a collection of structure quizzes
for SwiftUI’s LazyVGrid
to spotlight a few of the much less apparent behaviors. On this publish we’ll check out all three quiz questions and clarify why the grid lays out its contents in the way in which 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 hard and fast and an adaptive column. The grid has a hard and fast width of 200 factors:

								LazyVGrid(columns: [
    GridItem(.fixed(70)),
    GridItem(.adaptive(minimum: 40))
]) {
    
}
.body(width: 200)
.border(Coloration.white)

							

The three options we requested you to select from:

Resolution A is appropriate.

For grids, fastened measurement columns are at all times rendered precisely on the specified width, regardless of how a lot area is offered. Subsequently, the primary column renders as precisely 70 factors broad.

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 objects as attainable 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 will match two columns (40 + 8 + 40) into the 122 factors. Attempting 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 objects which can be 57 broad.

#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(Coloration.white)

							

The three potential options:

Resolution C is appropriate.

In contrast to the primary instance, this grid does not have any fastened columns, so the grid begins out with an obtainable 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 (non-fixed) 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 count on this consequence 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 objects inside that column. The adaptive column thus turns into 52 factors broad.

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 obtainable 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(Coloration.white)

							

The three potential options:

Resolution A is appropriate.

This seemingly easy structure reveals maybe essentially the most complicated conduct of the three examples on this publish. Not solely does the grid render out of bounds — though the columns’ minimums would fortunately match into the obtainable width of 200 factors — it additionally renders out of middle of its enclosing 200-points-wide body.

Let’s undergo the steps of the grid’s structure algorithm and see what’s taking place. 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. For the reason that 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 broad.

Nevertheless, that is not what we see within the rendering of this grid: the primary column renders 108 factors broad, the second renders 120 factors broad, whereas we calculated 96 factors and 120 factors.

To grasp this half we’ve got to do not forget 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 (224-200)/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 really 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 due to this fact keep away from this surprising conduct, whereas grids do not appear to do 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.

For the reason that 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 general width of 108+8+120 = 236 factors, the grid seems out of middle by 6 factors.

Conclusion

In abstract, these are the steps the grid’s structure algorithm takes:

  1. Begin out with the proposed width because the remaining width.

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

  3. 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.

Remember 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 mother or father view, whereas throughout rendering it begins with the calculated general width from the preliminary structure go.

This conduct may be fairly unintuitive, however we hope that this publish will enable you perceive that behaviour higher, and obtain the outcomes you goal for.

We’ll be persevering with the SwiftUI Format Defined
collection by way of December. Every episode re-implements a side of the structure system, and with six episodes already launched there’s lots to be taught!

To help us, and entry our complete catalogue, subscribe right here
.