iPhoneの写真アプリのように、画像などをタイル上に並べてレイアウトするいわゆるGridLayoutの方法を説明します。
環境
- Xcode12.5.1
- iOS14.6
- スクショはiPhone12Pro Maxで撮影
まずは縦に並べる方法を確認
GridLayoutを実現するにはListを使用します。なのでまずはListの使用方法をおさらいしましょう。
struct ContentView: View {
    var body: some View {
        List(1..<11) { _ in
            Image("Image")
        }
    }
}
縦に並べるだけならこんな感じで簡単に実装できます。ListではなくVStackを使用してもOKです。
水平に並べる
では次に画像を水平に並べてみましょう。
水平に並べるにはHStackを使用します。
struct ContentView: View {
    var body: some View {
        HStack {
            ForEach(1..<6) { _ in
                Image("Image")
            }
        }
    }
}
これもとても簡単ですね。これでは横スクロールはできませんが、今回は横スクロールは不要なので説明は省きます。
縦横を組み合わせる
これらを組み合わせることで以下のような見た目になります。
struct ContentView: View {
    var body: some View {
        List(1..<11) { _ in
            HStack {
                ForEach(1..<6) { _ in
                    Image("Image")
                }
            }
        }
    }
}
とりあえずこれで縦横に画像を並べることができました。
サイズの調整
並べることはできましたが、画面のサイズに対して画像が大きすぎますよね。横にちょうど5つ並ぶように調整してみましょう。
横にちょうど5つ並べるためには、画像のサイズを画面横幅÷5にすれば良さそう。ということで以下のように、GeometryReaderを使って実装します。
struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            List(1..<11) { _ in
                HStack {
                    ForEach(1..<6) { _ in
                        Image("Image")
                            .resizable()
                            .frame(width: geometry.frame(in: .global).width/5 , height: geometry.frame(in: .global).width/5)
                    }
                }
            }
        }
    }
}
それっぽくなってきましたが、5つ目の画像が切れてしまってます。そうです、余白分を考慮しないとちょうどいい感じに表示されません。
余白をなくす
では余白を無くしましょう。
ポイントは2つです。
まず、Listによってできてしまう余白を削除するために.listRowInsets(EdgeInsets())を使用します。この時、List内の表示にはForEachを使用してください。でないと.listRowInsets(EdgeInsets())が効きません。
もう一つはHStackでできてしまう余白を消すためにHStack(spacing: 0)を指定することです。
struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            List {
                ForEach(1..<11) { _ in
                    HStack(spacing: 0) {
                        ForEach(1..<6) { _ in
                            Image("Image")
                                .resizable()
                                .frame(width: geometry.frame(in: .global).width/5 , height: geometry.frame(in: .global).width/5)
                        }
                    }
                    .listRowInsets(EdgeInsets())
                }
            }
        }
    }
}
Listの横線を消す
これでほぼほぼ完成ですが、最後に不要な横線を消しましょう。
これが意外と面倒です。iOS15以降でしたら.listRowSeparator(.hidden)を設定してあげれば済むのですが、iOS14以前ではそうはいきません。
iOS14を対象にしたい場合はListではなくVStackを使用しましょう
struct ContentView: View {
    var body: some View {
        GeometryReader { geometry in
            ScrollView {
                VStack(spacing: 0) {
                    ForEach(1..<11) { _ in
                        HStack(spacing: 0) {
                            ForEach(1..<6) { _ in
                                Image("Image")
                                    .resizable()
                                    .frame(width: geometry.frame(in: .global).width/5 , height: geometry.frame(in: .global).width/5)
                            }
                            
                        }
                        .listRowInsets(EdgeInsets())
                    }
                }
            }
        }
    }
}
iOS13以前だけが対象なら、UITableView.appearance().separatorStyle = .noneを指定することで消すことができます。
struct ContentView: View {
    init() {
        UITableView.appearance().tableFooterView = UIView()
        UITableView.appearance().separatorStyle = .none
    }
    
    var body: some View {
        GeometryReader { geometry in
            List {
                ForEach(1..<11) { _ in
                    HStack(spacing: 0) {
                        ForEach(1..<6) { _ in
                            Image("Image")
                                .resizable()
                                .frame(width: geometry.frame(in: .global).width/5 , height: geometry.frame(in: .global).width/5)
                        }
                    }
                    .listRowInsets(EdgeInsets())
                }
            }
        }
    }
}おわり
今回は以上です。VStackとHStackの組み合わせで表示するのが無難ですね。
 
 

コメント