LoginSignup
4

More than 1 year has passed since last update.

posted at

Organization

LazyがないiOS13の世界でどうページネーション処理を作るか?

この記事はDiverse Advent Calendar 2020 20日目の記事になります:santa:

この記事について

SwiftUIが発表してから1年近く経ちましたね!
ios14からLazyが登場しSwiftUIでReuseの概念が追加されました!
これによりonAppearをトリガーに次ページを取得しやすいようになったかと思います。

しかし。。。
ios13の世界にはLazyが存在しない中
どのようにページネーション処理を行うのか?について今回書いてみます。
※ios13でListはLazyっぽい挙動な気がする:joy:

ios14ではどうやる?

Lazyが存在してるので画面に表示されたコンテンツのonAppearで
取得済みのデータと照らし合わせて次のデータを取得するイメージです。

例にはないですがLazyHStack,LazyHGridも同様です。

LazyVStack

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVStack(spacing: 8) {
                ForEach(0..<20) { number in
                    ListCell(number: number)
                        .onAppear() {
                            print("\(number)")
                        }
                }
            }
        }
    }
}

LazyVGrid

struct ContentView: View {
    var body: some View {
        ScrollView {
            LazyVGrid(columns: Array(repeating: GridItem(), count: 2), spacing: 16) {
                ForEach(0..<20) { number in
                    GridCell(number: number)
                        .onAppear() {
                            print("\(number)")
                        }
                }
            }
        }
    }
}

ios13では?

List

Listだけはios14のLazyと同様の方法で
onAppearでフックして次のデータを取得する処理を書くことが可能です。

import SwiftUI
struct ContentView: View {
    var body: some View {
        List {
            ForEach(0..<20) { number in
                ListCell(number: number)
                    .onAppear() {
                        print("\(number)")
                    }
            }
        }
    }
}

VStack

まず最初にios14のLazyVStack同様にonAppearを使って見る場合どうなるか?についてです。

struct ContentView: View {
    var body: some View {
        ScrollView {
            VStack(spacing: 8) {
                ForEach(0..<20) { number in
                    ListCell(number: number)
                        .onAppear() {
                            print("\(number)")
                        }
                }
            }
        }
    }
}

この場合は画面に表示されてないnumberまでもprint出力されてしまいます(0~19):sweat:
これだと次ページを取得するタイミングが画面が表示されてた時になってしまい想定と違うため
ios13のVStackでonAppearをトリガーに次ページを取得するのは難しいかと思います。

Scroll量をトリガーにする

ios13でLazyを持たないVStack,HStack,Grid(QGridでページネーションを行う例です。

一番下まであとどれくらいか?をトリガーに次のデータを取得するイメージです。

TODO
細かな説明書くと間に合わなさそうなので後ほど書いてきます:bow:

詳細は一旦Paginationリポジトリをのぞいていただけますと幸いです:bow:

struct ContentView: View {
    @State var contentOffset: CGFloat = .zero
    @State var scrollViewContentHeight: CGFloat = .zero
    var body: some View {
        GeometryReader { geometry in
            ScrollTrackerView(contentOffset: self.$contentOffset) {
                VStack(spacing: 8) {
                    ForEach(0..<10) { number in
                        ListCell(number: number)
                    }
                }.modifier(ScrollContentHeightKey())
            }.contentOffsetChanged {
                let maximumOffset: CGFloat = self.scrollViewContentHeight - geometry.size.height
                let distanceToBottom: CGFloat = maximumOffset - self.contentOffset
                print("\(distanceToBottom)")
            }.onPreferenceChange(ScrollContentHeightKey.self) {
                self.scrollViewContentHeight = $0
            }
        }
    }
}

ios13かつGridの場合

Gridは本家QGridをForkしたこちらを使用していただけると:v:

スクリーンショット 2020-12-20 15.33.57(2).png

Commit: 0bef369f0e96d46276394aecee1d98580ed7d362
↑こちら指定お願いします

本家と違うのは
contentOffsetChangedで下までの値がvalueとして
返ってくるcallback関数を用意してるところになります。
Pagination
↑Gridの例もこちらのリポジトリに乗せてありますのでよかったらぜひ:blush:

var body: some View {
    QGrid(self.data,
          columns: 2,
          vSpacing: 24.0,
          hSpacing: 15.0,
          contentOffsetChanged: { value in
            print("\(value)")
          }) { data in
            Button(
                action: {
                },
                label: {
                    GridCell(name: data.name)
                }
            ).buttonStyle(PlainButtonStyle())
    }
}

まとめ

ページネーションするかどうかの判定について
ios14ではLazyが使えるのでonAppear
ios13ではListはonAppear,VStackなどLazyがないViewに関してはスクロール量を使うと良いかと思います!

他にもこういう方法があるよっていうのあれば知りたいのでコメントいただけると幸いです:smile:

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
What you can do with signing up
4