WrappingHStack icon indicating copy to clipboard operation
WrappingHStack copied to clipboard

Item sizes are calculated incorrectly when WrappingHStack has modifiers that change the size of its elements

Open mylogon341 opened this issue 3 years ago • 5 comments

In the preview window you can add .environment(\.sizeCategory, .accessibilityMedium) to the modifier. This will emulate larger accessible fonts.

This will cause truncated views, like so Screenshot 2022-02-21 at 12 27 43 pm

mylogon341 avatar Feb 21 '22 12:02 mylogon341

This is not completely accurate, the problem is with modifiers outside WrappingHStack only. To measure the elements, type erasure is performed and the element is removed from the View hierarchy. Which means that modifiers on elements above the WrappingHStack will be ignored (wich may result in wrong sizes). With the current implementation, having to compute the whole hierarchy each time probably would be too time consuming (also, I'm not sure how I would pull that off). Maybe changing the way the NewLine element works could fix that.

Workaround: Set all necessary modifiers + environment variables directly on the items within WrappingHStack.

For example, in the librarie's example, adding .environment(\.sizeCategory, .accessibilityLarge) to the WrappingHStack that acts like a ForEach:

WrappingHStack(1...9, id:\.self, alignment: alignment, spacing: spacing) {
    Text("Item: \($0)")
        .padding(.all, 12)
        .background(RoundedRectangle(cornerRadius: 10).stroke())
}
.frame(width: 380)
.environment(\.sizeCategory, .accessibilityLarge)

will result in wrong sizes: Screenshot 2022-03-05 at 10 12 43

but if you set it on the item labels Text("Item: \($0)"):

WrappingHStack(1...9, id:\.self, alignment: alignment, spacing: spacing) {
    Text("Item: \($0)")
        .padding(.all, 12)
        .background(RoundedRectangle(cornerRadius: 10).stroke())
        .environment(\.sizeCategory, .accessibilityLarge)
}
.frame(width: 380)

it will result in the correct sizes: Screenshot 2022-03-05 at 10 09 48

dkk avatar Mar 05 '22 09:03 dkk

Another example where I encountered this problem:

WrappingHStack(lineSpacing: 10) {
    Toggle("1", isOn: $filter.filterByPlayers[0])
    Toggle("2", isOn: $filter.filterByPlayers[1])
    Toggle("3", isOn: $filter.filterByPlayers[2])
    Toggle("4", isOn: $filter.filterByPlayers[3])
    Toggle("5", isOn: $filter.filterByPlayers[4])
    Toggle("6", isOn: $filter.filterByPlayers[5])
}.toggleStyle(.button)

breaks after 4 (on iPhone 8 Plus size) while

WrappingHStack(lineSpacing: 10) {
    Toggle("1", isOn: $filter.filterByPlayers[0]).toggleStyle(.button)
    Toggle("2", isOn: $filter.filterByPlayers[1]).toggleStyle(.button)
    Toggle("3", isOn: $filter.filterByPlayers[2]).toggleStyle(.button)
    Toggle("4", isOn: $filter.filterByPlayers[3]).toggleStyle(.button)
    Toggle("5", isOn: $filter.filterByPlayers[4]).toggleStyle(.button)
    Toggle("6", isOn: $filter.filterByPlayers[5]).toggleStyle(.button)
}

correctly shows all 6 items in one row.

kassi avatar Jul 10 '22 15:07 kassi

Another instance of probably the same issue. IMG_1F3D19D8E3E4-1

lieanquintos avatar Nov 10 '22 22:11 lieanquintos

@lieanquintos without code this doesnt really say much

dkk avatar Nov 11 '22 06:11 dkk

To make a workaround for my case with a Toggle + Togglestyle, I added fixedSize to text as...

Text(myTextvar)
          .fixedSize(horizontal: true, vertical: false)

I had to add padding to the WrappingHStack on the right to ensure correct wrapping.

scoobeesnac avatar Jan 22 '24 13:01 scoobeesnac