Swift・iOS

Swiftを中心に学んだことを記録に残すブログです。技術に関係ない記事もたまに書いています。

【SwiftUI】LazyVGridを使ってUICollectionViewのようなUIを表現する

 

はじめに

UIKitでUICollectionViewを使って表現していたグリッドデザインを、SwiftUIで表現する方法について調べたので記事に残します。

 

開発環境

 

本題:LazyVGridでグリッドデザインを表現する

LazyVGridを使ってグリッドデザインを表現できる。

※参考:https://developer.apple.com/documentation/swiftui/lazyvgrid

 

並べ方に関して

アイテムを詰めて並べる

以下はアイコンをLazyVGridを使って並べたサンプル。GridItemのサイズ設定をadaptiveにすることで、指定した範囲内でアイコンを詰めて表示している。

ContentView.swift

import SwiftUI

struct Symbol: Identifiable {
    var id = UUID()
    var image: String
    var name: String
}

struct ContentView: View {
    let symbols = [Symbol(image: "drop.fill", name: "drop"),
                   Symbol(image: "flame.fill", name: "flame"),
                   Symbol(image: "bolt.fill", name: "bolt"),
                   Symbol(image: "leaf.fill", name: "leaf"),
                   Symbol(image: "hare.fill", name: "hare"),
                   Symbol(image: "tortoise.fill", name: "tortoise")
    ]
    
    let columns: [GridItem] = [GridItem(.adaptive(minimum: 100, maximum: 150))]
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 20) {
                ForEach(symbols) { symbol in
                    VStack {
                        Image(systemName: symbol.image)
                        Text(symbol.name)
                    }
                }
            }
            .padding()
        }
    }
}

f:id:hfoasi8fje3:20210425141700p:plain

 

※adaptiveの挙動に関して

adaptiveの「詰めて表示」の例として、adaptiveの最小値を80に変更してみる。そうすると、以下のような見た目になる。

f:id:hfoasi8fje3:20210425153503p:plain

 

adaptiveの最小値を60に変更すると以下。

f:id:hfoasi8fje3:20210425153617p:plain

 

1行あたりのアイテム数を指定して並べる

GridItemのcountで1行あたりのアイテム数を設定できる。

ContentView.swift

import SwiftUI

struct Symbol: Identifiable {
    var id = UUID()
    var image: String
    var name: String
}

struct ContentView: View {
    let symbols = [Symbol(image: "drop.fill", name: "drop"),
                   Symbol(image: "flame.fill", name: "flame"),
                   Symbol(image: "bolt.fill", name: "bolt"),
                   Symbol(image: "leaf.fill", name: "leaf"),
                   Symbol(image: "hare.fill", name: "hare"),
                   Symbol(image: "tortoise.fill", name: "tortoise")
    ]
    
    // 1行あたり3つのアイコンを表示する
    let columns: [GridItem] = Array(repeating: .init(.flexible(minimum: 60)), count: 3)
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 20) {
                ForEach(symbols) { symbol in
                    VStack {
                        Image(systemName: symbol.image)
                        Text(symbol.name)
                    }
                }
            }
            .padding()
        }
    }
}

f:id:hfoasi8fje3:20210425155354p:plain

 

余白の設定に関して

行間の余白を設定

LazyVGridのspacingで行間の余白を設定できる。

ContentView.swift

import SwiftUI

struct Symbol: Identifiable {
    var id = UUID()
    var image: String
    var name: String
}

struct ContentView: View {
    let symbols = [Symbol(image: "drop.fill", name: "drop"),
                   Symbol(image: "flame.fill", name: "flame"),
                   Symbol(image: "bolt.fill", name: "bolt"),
                   Symbol(image: "leaf.fill", name: "leaf"),
                   Symbol(image: "hare.fill", name: "hare"),
                   Symbol(image: "tortoise.fill", name: "tortoise")
    ]
    
    let columns: [GridItem] = Array(repeating: .init(.flexible(minimum: 60)), count: 3)
    
    var body: some View {
        ScrollView {
            // spacingで行間の余白を設定
            LazyVGrid(columns: columns, spacing: 200) {
                ForEach(symbols) { symbol in
                    VStack {
                        Image(systemName: symbol.image)
                        Text(symbol.name)
                    }
                }
            }
            .padding()
        }
    }
}

f:id:hfoasi8fje3:20210425160051p:plain

 

LazyVGridの上下左右の余白を設定

これはLazyVGridに限らないが、paddingで設定できる。

ContentView.swift

import SwiftUI

struct Symbol: Identifiable {
    var id = UUID()
    var image: String
    var name: String
}

struct ContentView: View {
    let symbols = [Symbol(image: "drop.fill", name: "drop"),
                   Symbol(image: "flame.fill", name: "flame"),
                   Symbol(image: "bolt.fill", name: "bolt"),
                   Symbol(image: "leaf.fill", name: "leaf"),
                   Symbol(image: "hare.fill", name: "hare"),
                   Symbol(image: "tortoise.fill", name: "tortoise")
    ]
    
    let columns: [GridItem] = Array(repeating: .init(.flexible(minimum: 60)), count: 3)
    
    var body: some View {
        ScrollView {
            LazyVGrid(columns: columns, spacing: 200) {
                ForEach(symbols) { symbol in
                    VStack {
                        Image(systemName: symbol.image)
                        Text(symbol.name)
                    }
                }
            }
            // 画面端からの余白を設定
            .padding(100)
        }
    }
}

f:id:hfoasi8fje3:20210425160438p:plain


画面遷移に関して

画面遷移の実装は、(これもLazyVGridに限らないが)NavigationLinkを使って実現できる。

ContentView.swift

import SwiftUI

struct Symbol: Identifiable {
    var id = UUID()
    var image: String
    var name: String
}

struct ContentView: View {
    let symbols = [Symbol(image: "drop.fill", name: "drop"),
                   Symbol(image: "flame.fill", name: "flame"),
                   Symbol(image: "bolt.fill", name: "bolt"),
                   Symbol(image: "leaf.fill", name: "leaf"),
                   Symbol(image: "hare.fill", name: "hare"),
                   Symbol(image: "tortoise.fill", name: "tortoise")
    ]
    
    let columns: [GridItem] = Array(repeating: .init(.flexible(minimum: 80)), count: 3)
    
    var body: some View {
        NavigationView {
            ScrollView {
                LazyVGrid(columns: columns, spacing: 30) {
                    ForEach(symbols) { symbol in
                        NavigationLink(
                            destination: DetailView(symbol: symbol),
                            label: {
                                VStack {
                                    Image(systemName: symbol.image)
                                    Text(symbol.name)
                                }
                            })
                    }
                }
                .padding()
            }
            .navigationTitle("アイコン一覧")
        }
    }
}

 

DetailView.swift

import SwiftUI

struct DetailView: View {
    var symbol: Symbol
    
    var body: some View {
        VStack {
            Image(systemName: symbol.image)
            Text(symbol.name)
        }
    }
}

f:id:hfoasi8fje3:20210425162123g:plain

 

おわりに

簡単にグリッドデザインを表現できるのはよいと思いました。今後、実践を通してさらに理解度を上げていこうと思います。

 

参考