🔧

iOS15にてLazyVGrid子要素の高さが揃わない問題と対策

2022/04/21に公開

iOS14.5では、LazyVGridの子要素は高さが揃いますが
iOS15からは、おそらくバグで高さが揃わなくなっています。

StackOverflowにも同様の報告上がっています。
https://stackoverflow.com/questions/68434369/how-to-make-every-item-the-same-height-in-a-lazyvgrid

LazyVGridで解決するのは難しい可能性が高いので
今回はHStackを利用しました。

import SwiftUI

struct MaximumHeightPreferenceKey: PreferenceKey {
    static var defaultValue: CGFloat = 0
    static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
        value = max(value, nextValue())
    }
}

struct DetermineHeight: View {
    typealias Key = MaximumHeightPreferenceKey
    var body: some View {
        GeometryReader { proxy in
            Color.clear
                .anchorPreference(key: Key.self, value: .bounds) { anchor in
                    proxy[anchor].size.height
                }
        }
    }
}

struct ContentView: View {
    @State var maximumSubViewHeight: CGFloat = 0

    let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 2)

    var body: some View {
        HStack {
            Group {
                Text("Hello")
                    .border(.red)
                    .overlay(DetermineHeight())
            }
            .frame(minHeight: maximumSubViewHeight, alignment: .bottom)
            Group {
                Text("Lorem\nipsum")
                    .border(.blue)
                    .overlay(DetermineHeight())
            }
            .frame(minHeight: maximumSubViewHeight, alignment: .bottom)
        }
        .border(.green)
        .padding(.horizontal, 100)
        .onPreferenceChange(DetermineHeight.Key.self) {
            maximumSubViewHeight = $0
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

※ このコードは大部分をWooji Juice社様の記事を参考にさせて頂いております。
https://www.wooji-juice.com/blog/stupid-swiftui-tricks-equal-sizes.html

PreferenceKeyやminHeightを利用して上手く問題に対処しているように見えます。
そして、このようにiOS14.5でもiOS15でも動作します。

ただ、HStackだとLazyVGridのように多数のデータを列に並べるなどを満たしません。

ここはLazyVGridに渡す配列の代わりに、配列を2つづchunk化して
HStackに例えば2個づつ渡すような実装で対応を行いました。

// コードのイメージ
ForEach(items.chunked(by: 2), id: \.id) { chunk in
  GirdPartView(chunk)
}

chunked実装は、このようなイメージなります。
https://gist.github.com/sumitokamoi/22b8f30c2c1a3ef93cb1f03d4a7e8066
https://www.hackingwithswift.com/example-code/language/how-to-split-an-array-into-chunks

Discussion