【SwiftUI】iOS16+のGrid|グリッドレイアウトの作り方

iOS16+から、SwiftUIの新規コンテナビュー(※1)「Grid」が登場しました。

グリッドという名の通り、”盤”状のような複数行からなる一塊のレイアウトを構築するのに便利な機能が用意されています。

👉ドキュメント: Grid | Apple Developer Documentation

VStack + HStackのような手触りでレイアウト構築ができますが、スタックビューとは異なる機能や制約がいくつか存在します。

本記事では、Gridの機能と使い方について、主にドキュメントを参考にしながら見ていきます。

(※1)コンテナビュー

複数の他のビューを包含し、まとめて表示するためのビューのことを指す。UIの構造を整理し、ビューの階層を管理するために使用される。

[ 本記事はこんな人におすすめ ]

・SwiftUIでアプリ開発をしている

・Gridの各機能と使い方について知りたい

シンプルなGridの実装コード

まずはシンプルなグリッドの実装コードを挙げます。

Grid構造体のクロージャ内に、一行を管理するGridRowを配置してレイアウトを作っていくのが基本的な形となります。

				
					/// Gridのシンプルな実装コード
struct BasicGrid: View {
    var body: some View {

        Grid {
            GridRow {
                ForEach(0..<3) { _ in Color.red }
            }
            .frame(width: 50, height: 50)
        }
    }
}
				
			

GridRow

グリッドレイアウト内における水平方向「一行」のビュー要素を管理します。

👉ドキュメント: GridRow | Apple Developer Documentation

				
					Grid {
    GridRow {
        // 一行目の要素
    }
    GridRow {
        // 二行目の要素
    }
    GridRow {
        // 三行目の要素
    }
}
				
			

Grid内に複数のGridRowを配置すれば、複数行のグリッドレイアウトを作成できます。

また、スタックビューと異なる点として、GridRow(一行)に対して付与した制御は、自身が持つビュー要素全てに対して適用されます。

以下は、スタックビューとグリッドビューの比較例です。

【グリッドビュー】

【スタックビュー】

要素(列)の数

GridRowのクロージャ内に配置したビューの数が、グリッド一行内の要素(列)数となります。

Grid

また、複数行のレイアウトにおいて、行ごとにセル/列の数が異なる場合、十分なビューが指定されていない行には空のセルが入ります。

以下のように、各行のセルは左から順に配置されていきます。

最も多い列数「5列」を基準に、足りない要素分は空白のセルが入る

要素のスペース

Grid(horizontalSpacing:verticalSpascing:)にグリッド内の各要素が取っているスペース幅を指定できます。

横・縦それぞれ個別で指定が可能です。

				
					/// 各要素/セルのスペース幅を指定
Grid(horizontalSpacing: 1, verticalSpacing: 1) {
    // ...
}
				
			

【デフォルト】

【適用後】

配置ルールの指定

グリッド全体の配置を指定

Grid(alignment:)にAlignment値を渡すことで、グリッド全体の要素配置ルールを設定できます。

				
					Grid(alignment: .leading) {
    // ...
}
				
			
グリッド全体の整列ルールが「.leading」となる

指定がない場合は、デフォルトで中央配置(.center)が適用されます。

デフォルトは中央(.center)

水平方向の配置を指定

GridRow(alignment:)にAlignment値を指定することで、任意行の整列ルールを制御できます。

ここで指定できるのは”水平方向”の整列ルールのみです。

この設定はGrid(alignment:)による全体の水平設定をオーバーライド(上書き)します。

				
					/// GridRow(alignment:)で行ごとの垂直方向の配置ルールを上書きできる
/// 指定がない場合、Grid(alignment:)に指定した配置ルールが適用される
Grid(alignment: .trailing) {
    GridRow(alignment: .top) { // 👈 トップ
        // ...
    }
    GridRow { // 👈 デフォルト
        // ...
    }
    GridRow(alignment: .bottom) { // 👈 ボトム
        // ...
    }
}
				
			

垂直方向の配置を指定(GridColumnsAlignment)

.gridColumnsAlignmentモディファイアをグリッド内の要素に付与することで、対象と同列の要素に対して整列ルールを付与できます。

ここで指定できるのは”垂直方向”の整列ルールのみです。

この設定はGrid(alignment:)による全体の垂直設定をオーバーライド(上書き)します。

				
					Grid(alignment: .leading) {
    GridRow {
        Text("価格:") // 適用
            .gridColumnAlignment(.trailing) // 同列要素の配置ルールを上書き
        Text("1000")
    }
    GridRow {
        Text("入荷日:") // 適用
        Text("1000")
    }
    GridRow {
        Text("売り上げ:") // 適用
        Text("25000")
    }
}
				
			

ポイントとして、一要素に対してモディファイアを付与すれば、同列の要素全てに対して配置ルールが適用されます。

同列の要素全てに整列ルールが適用される

位置ポイントの調整

セルの位置ポイント調整(GridCellAnchor)

.gridCellAnchorモディファイアをグリッド内の一要素に付与することで、対象ビューのセル領域の範囲内で、左上端を始点とした位置ポイントを設定できます。

				
					/// グリッド内要素の位置ポイントを調整する
Grid(horizontalSpacing: 1, verticalSpacing: 1) {
    GridRow {
        Color.red.frame(width: 60, height: 60)
        Color.red.frame(width: 60, height: 60)
    }
    GridRow {
        Color.red.frame(width: 60, height: 60)
        Color.blue.frame(width: 10, height: 10)
            .gridCellAnchor(UnitPoint(x: 0.25, y: 0.75)) // ⬅︎
    }
}
				
			

以下の例では、グリッド内のビュー要素一つの位置ポイントを、「x方向へ25%」「y方向へ75%」に指定しています。

自身が持つセル領域の左上端を始点に、「x方向に25%」「y方向に75%」を指定

また、「.top」「.bottom」や「.topTrailing」といったポイント指定も可能です。

位置ポイントに「.bottomTrailing」を指定

ポイントの指定がない場合、デフォルトで中央(.center)に配置されます。

セル/列を跨ぐビュー

指定数の列を跨ぐ(GridCellColumns)

GridRowのビュー要素に.gridCellColumnsモディファイアを付与することで、指定したセル/列の数を跨(また)いでビューが配置されます。

【適用前】

デフォルトでは一つのセル/列にビューが配置される

【適用後】

「2」を指定することで、ビューが二列のセルを跨いで配置される

グリッド内の全列を跨ぐビュー

Gridの中に単一のビューを配置すると、グリッドはそのビューを使用して、グリッド内のすべての列を跨ぐ行を新たに作成します。

				
					Grid {
    GridRow {
        // ...
    }
    
    Text("CodeCandy") // 👈 単一のビュー
    
    GridRow {
    
        // ...
    }
}
				
			

この際のポイントとして、単一ビューのフレーム領域がグリッド本来の必要幅よりも広いスペースを持っている場合、グリッド全体のレイアウトも引っ張られて広くなります。

以下の例は、画面横いっぱいまで広がる区切り線を配置した場合のグリッドの様子です。

				
					Grid {
    GridRow {
        // ...
    }
    
    Divider() // 👈 区切り線
    
    GridRow {
        // ...
    }
}
				
			
区切り線が横いっぱいまで領域を取ることで、グリッドも横に広がっている

単一ビューのスペース幅をグリッドの必要幅に収めたい場合、後述で紹介するGridCellUnsizedAxesを使用します。

GridCellUnsizedAxes

グリッドが本来持っているフレーム領域より多くのスペースを占めることを防ぐには、.gridCellUnsizedAxesモディファイアをビューに付与します。

				
					Divider()
    .gridCellUnsizedAxes(.horizontal)
				
			

これにより、Grid内に配置した単一のビューが、グリッドが本来持っている領域幅に収まるようになります。

グリッド本来の必要幅が復元される

Gridの用途と使うメリット

VStack + HStackとの違い

Grid + GridRowの「横並びのビューを縦に積んでいく」といったビューの組み立ては、VStack + HStackとよく似た手触りをしています。

				
					Grid {
    GridRow {
        // ...
    }
    GridRow {
        // ...
    }
    GridRow {
        // ...
    }
}
				
			
				
					VStack {
    HStack {
        // ...
    }
    HStack {
        // ...
    }
    HStack {
        // ...
    }
}
				
			

これら二つには、「一連ビューに置かれている”各行”に関係性があるかどうか」という点に違いがあります。

・スタックビュー → 各行(HStack)が独立している

・グリッドビュー → 各行(GridRow)が関係性を持っている

以下のサンプルを使って、各行の関係性を見てみます。

どちらも三列×三行で構成されたビューレイアウトです。

では上記の状態から、三行目のビュー要素を一つ削除し、二列にしてみます。

・スタックビュー側のHStackは、他の行とは独立して新しい位置を取ります。

・グリッドビュー側のGridRowは、残った要素が他の行との位置関係を保っています。

三行目の要素を一つ削除

次に、要素一つのサイズを変更してみます。

・スタックビュー側では、変更サイズを元に、他行の同列(縦の列)要素とのサイズ関係を無視して独立したビュー領域を取ります。

・グリッドビュー側では、同列(縦の列)の要素が、サイズ変更された要素に合わせて同じ幅のビュー領域を持ちます。また、残りの列も元の位置関係を保っていますね。

このように、一連のビュー内の各行および各列が、それぞれに影響し合うような関係性を持っているというのが、グリッドビューとスタックビューの違いとなります。

パフォーマンスについて

Gridは、初期化時に保持するビュー全てを直ちにローディングします。

これにより、SwiftUIに各セルの幅や高さを同時に計算させることができ、列と行の両方にわたって自動的にセルのサイズと位置を調整することができます。

しかし反面、処理が重たいレイアウト(大容量の画像描画など)を組もうとした場合、パフォーマンスの低下や一時的なフリーズが発生する可能性があります。

処理が重いレイアウトを構築する場合は、GridよりもLazyVStackLazyVGridなどの遅延ロード機能を持つビューの使用が適している場合があります。

まとめ

以上、iOS16+で登場したSwiftUIのコンテナビュー「Grid」についてでした!

要点をまとめておきます。

・Grid構造体の中に一行を管理するGridRowを配置し、レイアウトを構築していく

・各行が関係性を持っており、サイズや配置ルールがお互いに影響を及ぼす

・ビューの描画処理が軽く、各列ごとに共通の制御を渡したいような場合に適している

構築するレイアウトによって、スタックビューとグリッドビューのどちらが適しているかは違ってくるかと思います。

条件が合っていれば、スタックビューよりもスッキリとしたコードでグリッド状のレイアウトを制御できるので、うまく使い分けていきたいですね。

本記事が参考になれば幸いです。

本記事のソースコード

本記事で使用しているサンプルコードはGitHubにて公開されています。

コード内容の確認にぜひご利用ください🍎

ソースコードを見る>>

\  SHARE  /

Twitter
Facebook
Email

✏️ アプリ開発が学べる勉強会を開催中! 

CodeCandyではアプリ開発を学ぶための勉強会を定期開催しています。
学習する習慣を身につけたい、他の参加者と作業したい、アプリ開発の基本をマスターしたい、という方のために無料で学べる勉強会です。
グループにメンバー登録して頂くと、イベント開催時にメールで通知されます。

▶️グループのメンバーとして参加する

徹底した基礎学習からマスターするiPhoneアプリ開発集中オンライン講座開講!

本書「iPhoneアプリ開発集中講座」を執筆している現役エンジニア講師陣が直接に指導!
基礎、課題実習で実践力を鍛えて、オリジナルアプリ公開までチャレンジ!
充実した転職支援もあるので、エンジニアへ転職したい人にもおすすめです!

まずは、現役エンジニアに相談できる無料相談をご利用ください。

By 中川 賢亮

2022年2月よりSwift学習を始め、4月からiOSアプリ開発オンラインスクール「CodeCandy」にてアプリ開発を学ぶ。 2023年10月に個人開発アプリ「unico」をリリース。現在はアプリの機能アップデートをしながら、スクール運営の技術ブログの執筆や、出版書籍の入稿チェック・デバッグにも携わる。