10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUIでLazyなGridとStackの使い方を学ぶよ

Posted at

おはよう:relaxed:
今日はLazyVGrid,LazyVStack,LazyHGridLazyHStackの使い方を勉強するよ。

###Lazyってなにかざくっと勉強
Stackは画面を開いたときにその中の要素をすべて読み込み・描写します。
一方Lazy〜は必要に応じてViewを読み込み・描写をするという違いがあります。

例えば、表示したいViewのサイズが大きかったり、個数が多い場合、Stackで画面を作ってしまうと、パフォーマンスが低下する危険性があります。
Lazy〜を使うことで、必要に応じて読み込みと描写をさせ、処理を低下させずに画面描写を行うことができるようです。

Lazy〜はそれぞれiOS14以上から使用できる比較的新しい構造体です。

公式:Creating Performant Scrollable Stacks

###LazyVGridとLazyVStackの使い方

公式:LazyVGrid
公式:LazyVStack

【サンプルPGM】

LazyVGrid.swift
import SwiftUI


//2. GridItem
let columns: [GridItem]=[GridItem(),GridItem()]

struct LazyView: View {
    var body: some View {

        NavigationView {

            //3.ScrollView            
            ScrollView(.vertical) {

                /*---LazyVGridの表示---*/
                // 1.LazyVGrid

                LazyVGrid(columns: columns, pinnedViews: [.sectionHeaders]) {
                    
                    // header, footerで呼び出しているLazyPinnedViewは下のPGMで定義しています。
                    Section(header: LazyPinnedView(title: "GridHeader", color: Color.blue),
                            footer: LazyPinnedView(title: "GridFooter", color: Color.green)) {
                        
                        ForEach(0..<20) { i in
                            Text("Grid: \(i)")
                                .frame(width: 100, height: 50)
                                .background(Color(red: Double(i * 10) / 255, green: 1
                                                  ,blue: Double(i * 30) / 255 ))
                        }
                    }
                }
                



                /*---LazyVStackの表示---*/
                // 5.LazyVStack
                LazyVStack(spacing: 3, pinnedViews: [.sectionHeaders]) {

                    Section(header: LazyPinnedView(title: "ListHeader", color: Color.yellow),
                            footer: LazyPinnedView(title: "ListFooter", color: Color.red)) {
                        ForEach(0..<15) { i in
                            HStack {
                                Spacer()
                                Text("List: \(i)")
                                Spacer()
                            }.padding(.all, 30)
                            .background(Color(red: 1, green: Double(i * 30) / 255
                                              ,blue: Double(i * 10) / 255))
                        }
                    }
                    
                }
            }
            .navigationTitle("Navigation Header")
        }
        
    }
}

struct LazyView_Previews: PreviewProvider {
    static var previews: some View {
        LazyView()
    }
}


LazyPinnedView.swift
import SwiftUI

struct LazyPinnedView: View {
    private var title: String
    private var color: Color
    
    init(title: String, color : Color){
        self.title = title
        self.color = color
    }
    
    var body: some View {
        
        HStack {
            Spacer()
            Text("\(title)")
            Spacer()
        }.padding(.all, 10).background(color)
    }
}

struct LLazyPinnedView_Previews: PreviewProvider {
    static var previews: some View {
        LazyPinnedView(title:"previewHeader", color :Color.blue)
    }
}


【挙動】
demo1.gif

####1. LazyVGrid
LazyVGridは下記のような形で使用するよ。


LazyVGrid (columns: XXXX, PinnedScrollableViews : YYYY)

columns:XXXX
GridItem型に則って、1行のGridの個数や、1要素のサイズ・行間を指定する。
今回は1行のカラム数=GridItem2個で定義しているね。
これを3つにするとこのように変わります。

GridItem3

下記のような形で定義もできます。

let columns: [GridItem] = Array(repeating: .init(.flexible()), count: 4)
///Array(repeating: X-リピートしたい文字-X, count: Y-回数-Y)

PinnedScrollableViews:YYYY
[sectionHeaders][sectionFooters]のどちらかを指定。
リストをスクロールした際に固定される要素を決めているよ。
最初のサンプルはsectionHeadersを使用しています。
sectionFootersを選択した場合の挙動は下記のとおりです。

【挙動】
demo2.gif

####2.GridItem
公式:GridItem
1個1個のGridItemを定義するのに使うよ。
すべてのGridItemは個別にサイズや間隔を設定することができます。
サイズの設定の仕方としては、fixed, flexible,adaptiveの3つのやり方があります。

#####fixed
固定値としてサイズを設定します。
例えば下記のように、1行2カラムをそれぞれ固定値と間隔・寄せを設定した場合

let columns: [GridItem] = [
    GridItem(.fixed(150), spacing: 16, alignment: .top),
    GridItem(.fixed(200), spacing: 20, alignment: .leading)
]
GridItem2fixed

1行3カラムで、Frameサイズより小さくGridを設定した場合は、画像のように要素同士が重なって表示されるよ。

let columns: [GridItem] = [
    GridItem(.fixed(50),spacing: 16),
    GridItem(.fixed(70),spacing: 16),
    GridItem(.fixed(100),spacing: 16)
]
GridItem3fixed [公式:fixed(_:)](https://developer.apple.com/documentation/swiftui/griditem/size-swift.enum/fixed(_:))

#####flexible
空いているスペースに合わせて拡張したり縮小したりしてカラムサイズが変わる設定の仕方で、最小値と最大値を設定することができます。
デフォルト値は、minimum(最小)が10、maximum(最大)がinfinityのようです

1行2アイテムの場合

private var columns: [GridItem] = [
    GridItem(.flexible(maximum: 150)),
    GridItem(.flexible())
]
GridItem2flexible

1行3アイテムの場合

private var columns: [GridItem] = [
    GridItem(.flexible(minimum: 70, maximum: 150)),
    GridItem(.flexible(minimum: 70, maximum: 150)),
    GridItem(.flexible())
]
GridItem2flexible

公式:flexible(minimum:maximum:)

#####adaptive
flexibleは1行のなかで柔軟にアイテムを配置していましたが、adaptiveはその行にまだItemを入れるスペースがある場合は、行を超えて配置がされます。
なので、例えば下記のように設定しても1行3アイテムで詰めて表示がされます。

private var columns: [GridItem] = [
    GridItem(.adaptive(minimum: 100, maximum: 100)),
    GridItem(.adaptive(minimum: 100))
]
GridItem2flexible

公式:adaptive(minimum:maximum:)

####3.ScrollView
ScrollView(.vertical) ・・ 縦方向のスクロール
ScrollView(.horizontal) ・・ 横方向のスクロール
LazyVGrid, LazyVStack, LazyHStackは必要なアイテムだけ読み込むので、
ScrollViewの中に配置しないと、すべての要素が画面上に表示されなくなってしまうよ。

####4.LazyVStack
LazyVStackは下記のように使うよ。
alignmentやspacingなどの要素はすべて任意なので、不要であればなしでOKだよ。
pinnedViewsはLazyVGridと同じ使い方をするよ。headerとfooterでの固定され方の違いは前のgifをみてね。

LazyVStack(alignment: XXXX, spacing:XXXX, pinnedViews: XXXX,content: XXXX) {

###LazyHGrid, LazyHStackの使い方

公式:LazyHGrid
公式:LazyHStack

基本的な使い方はLazyVGrid, LazyVStackと同じです。
今回は比較のため、最初のPGMをLazyH~用に最低限に書き換えているので、
すこしレイアウトがおかしいですが、ご了承ください。

LazyView.swift
let rows: [GridItem] = [GridItem(),GridItem()]

struct LazyView: View {
    var body: some View {
        NavigationView {

            // 変更点1
            ScrollView(.horizontal) {
                VStack {

                    // 変更点2
                    LazyHGrid(rows: rows, alignment: .top, pinnedViews: [.sectionHeaders]) {
                        
                        Section(header: LazyPinnedView(title: "GridHeader", color: Color.blue),
                                footer: LazyPinnedView(title: "GridFooter", color: Color.green)) {
                            
                            ForEach(0..<20) { i in
                                Text("Grid: \(i)")
                                    .frame(width: 100, height: 50)
                                    .background(Color(red: Double(i * 10) / 255, green: 1
                                                      ,blue: Double(i * 30) / 255 ))
                            }
                        }
                    }
                    //変更点3
                    LazyHStack(spacing: 3, pinnedViews: [.sectionHeaders]) {
                        Section(header: LazyPinnedView(title: "ListHeader", color: Color.yellow),
                                footer: LazyPinnedView(title: "ListFooter", color: Color.red)) {
                            ForEach(0..<10) { i in
                                HStack {
                                    Spacer()
                                    Text("List: \(i)")
                                    Spacer()
                                }.padding(.all, 30)
                                .background(Color(red: 1, green: Double(i * 30) / 255
                                                  ,blue: Double(i * 10) / 255))
                            }
                        }
                        
                    }
                }
            }
            .navigationTitle("Navigation Header")
        }
        
    }
}

Header固定
【挙動】
demo4.gif

Footer固定
【挙動】
demo5.gif

####変更点1 ScrollView
LazyV〜の場合は縦方向のスクロールverticalを設定していましたが、
LazyH〜の場合は横方向のスクロールhorizontalに変更。

####変更点2 LazyHGrid
LazyVGridのときはcolumnsで1行あたりのカラム数を設定していましたが、
LazyHGridのときは、行数を設定しています。
どちらもGridItemを必要な個数配置して、設定しています。

####変更点3 LazyHStack
単にLazyVStack→LazyHStackで置き換えただけです。同様な設定で使用できます。

###まとめ
今回はLazy系の機能を使ってみて、動きや作り方を学んだよ。
headerが固定されます!とか、Gridの個数を設定できます!とか、実際動かしてみると、
理解が深まった気がしました。

理解に誤りがあれば、ご指摘いただけると幸いです:relaxed:

10
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?