SwiftUIでCollectionViewを表示する方法【QGrid】

UIKit の UICollectionView を SwiftUI でもやってみようと思ったところ、どうもそれらしき代替品が見つかりませんでした(2020/04/29現在)※Xcode12&iOS14から LazyHGrid と LazyVGrid で実現できるようになりました。詳しくはこちらの記事をどうぞ!

ListHStackVStack などを使ってそれっぽいものを作れないかなあ〜、とあれこれやっていたところ、

既にオープンソースで公開されているものがあるではないですか!

https://github.com/Q-Mobile/QGrid

ここは意地を張らずに、先人の知恵をお借りしよう!

と言うことで今回は『QGrid』について紹介します。

猫の画像引用元:みんなの猫図鑑

SwiftUIでCollectionViewを表示する方法【QGrid】

QGrid は GitHub で公開されているオープンソースパッケージです。Xcode のプロジェクトを作成しても、最初から使うことは出来ません。プロジェクトで import するには、Swift Package Manager を使ってダウンロードしてくる必要があります。

Swift Package Manager を使ったパッケージの導入手順については、下記の記事で紹介していますので、まだ使ったことがない方はまず手順に沿って QGrid をXcodeプロジェクトに追加してください。

QGrid を Swift Package に追加出来たら、早速、QGrid を使用するソースコード上で import しましょう。

import QGrid

続いて、イニシャライザーについて確認します。イニシャライザーは2種類あります。

QGrid(data: _, columns: Int, content: (_.Element) -> _)
QGrid(data: _, columns: Int, columnsInLandscape: Int?, vSpacing: CGFloat, hSpacing: CGFloat, vPadding: CGFloat, hPadding: CGFloat, isScrollable: Bool, showScrollIndicators: Bool, content: (_.Element) -> _)
  • data: _ 任意の型のデータ配列を渡します
  • columns: Int 横(列)の数を指定します
  • columnsInLandscape: Int? 端末を横向きにした時の横(列)の数を指定します
  • vSpacing: CGFloat 縦(行)の表示間隔を指定します
  • hSpacing: CGFloat 横(列)の表示間隔を指定します
  • vPadding: CGFloat QGrid 全体の上下の余白を指定します
  • hPadding: CGFloat QGrid 全体の左右の余白を指定します
  • isScrollable: Bool スクロールさせるかどうか指定します
  • showScrollIndicators: Bool スクロールバー(インディケータ)の表示・非表示を指定します
  • content: (.Element) -> セルとなるコンテンツ(View)を指定します。

それでは、冒頭画像のソースコードを確認していきます。

Cat構造体

まずはデータ型の定義です。Identifiable に準拠しておく必要があります。

struct Cat: Identifiable {
    let id: Int
    let imageName: String
    let title: String
}

GridCell

セルのViewを定義します。

今回は画像(Image)とタイトル(Text)の組み合わせで、Cat構造体のデータを持たせています。

struct GridCell: View {
    
    let cat: Cat
    
    var body: some View {
        VStack {
            Image(self.cat.imageName)
                .resizable()
                .frame(width: 100, height: 100)
                .scaledToFit()
                .clipShape(Circle())
            VStack {
                Spacer()
                Text(self.cat.title)
                    .font(.caption)
                    .multilineTextAlignment(.center)
                Spacer()
            }
        }
    }
}

ContentView(QGrid)

最後は、実際に QGrid を使っている ContentView の内容です。

今回はテストのため Cat を静的なデータとして直接持たせていますが、通常はViewModel等で管理しておくと良いでしょう。

struct ContentView: View {
    
    let cats: [Cat] = [
        Cat(id: 1, imageName: "cat_img_scottish-fold", title: "スコティッシュフォールド"),
        Cat(id: 2, imageName: "cat_img_munchkin", title: "マンチカン"),
        Cat(id: 3, imageName: "cat_img_norwegian-forest-cat", title: "ノルウェージャンフォレストキャット"),
        Cat(id: 4, imageName: "cat_img_mainecoon", title: "メインクーン"),
        Cat(id: 5, imageName: "cat_img_bengal", title: "ベンガル"),
        Cat(id: 6, imageName: "cat_img_british-shorthair", title: "ブリティッシュショートヘア"),
        Cat(id: 7, imageName: "cat_img_ragdoll", title: "ラグドール"),
        Cat(id: 8, imageName: "cat_img_exotic-shorthair", title: "エキゾチックショートヘア"),
        Cat(id: 9, imageName: "cat_img_minuet", title: "ミヌエット"),
        Cat(id: 10, imageName: "cat_img_american-shorthair", title: "アメリカンショートヘア")
    ]
    
    var body: some View {
        QGrid(self.cats,
              columns: 3,
              columnsInLandscape: 5,
              vSpacing: 16,
              hSpacing: 8,
              vPadding: 16,
              hPadding: 16,
              isScrollable: true,
              showScrollIndicators: false
        ) { cat in
            GridCell(cat: cat)
        }
    }
}

縦向きの場合、列数は3つ、横向きになると5つの表示になります。

また、上下左右の余白に 16pt を設け、列間隔を 8pt 、行間隔を 16pt としています。

以上で QGrid の使い方を紹介しました。

iOS非公式のオープンソースではありますが、取り急ぎ SwiftUI で CollectionView を実現するには利用しない手はありません。

とは言え、UICollectionView のようにiOS公式のレイアウト要素としてリリースして貰いたいものですね。

おわり