【SwiftUI】簡単にできるタイル表示 – GridLayout【コピペで使える】

List

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の組み合わせで表示するのが無難ですね。

コメント

タイトルとURLをコピーしました