UIKit ではテーブルデータのリロード機能の実装に UIRefreshControl を使用していました。
SwiftUI では代わりとなる UI がまだ提供されていません(2021年4月現在)。
UIRefreshControl をラップした UIViewRepresentable プロトコルに沿った View を作成することで実現することができるようですが、
純粋な SwiftUI の機能の組み合わせだけで実現することもできます。
【SwiftUI】Pull to refresh(UIRefreshControl)を実装する
以下のような RefreshControl という View 部品を作成しました。
struct RefreshControl: View {
@State private var isRefreshing = false
var coordinateSpaceName: String
var onRefresh: () -> Void
var body: some View {
GeometryReader { geometry in
if geometry.frame(in: .named(coordinateSpaceName)).midY > 50 {
Spacer()
.onAppear() {
isRefreshing = true
}
} else if geometry.frame(in: .named(coordinateSpaceName)).maxY < 10 {
Spacer()
.onAppear() {
if isRefreshing {
isRefreshing = false
onRefresh()
}
}
}
HStack {
Spacer()
if isRefreshing {
ProgressView()
} else {
Text("⬇︎")
.font(.system(size: 28))
}
Spacer()
}
}.padding(.top, -50)
}
}
ポイントは、GeometryReader です。
GeometryReader は、親View の位置や、子View の相対的な位置などを取得するための View の一種です。
GeometryReader についてはこちらの Qiita 記事が大変参考になりますのでご一読ください。
この RefreshControl は ScrollView の子View として利用します。GeometryReader 情報(GeometryProxy)を使って親である ScrollView の位置を把握し、PullToRefreshの動きをコントールしています。
また、RefreshControl に取っては親がどの View なのかはわからないため、予め親View の frame に名前(coordinateSpaceName)を設定し、その名前の frame を元に判断をしています。
PullToRefresh の簡単な流れは以下のようになります。
- ScrollView を下に引っ張る
- ScrollView の Y位置が 50 を超えたら isRefreshing が true になる
- ScrollView から指を離す
- ScrollView の Y位置が 10未満になった時 isRefreshing が true になっていたら onRefresh を実行
実際に利用する時は以下のように使います。
struct ContentView: View {
var body: some View {
ScrollView {
VStack {
RefreshControl(coordinateSpaceName: "RefreshControl", onRefresh: {
print("doRefresh()")
})
Text("test01")
Text("test02")
Text("test03")
Text("test04")
Text("test05")
Text("test06")
Text("test07")
Text("test08")
Text("test09")
}
}.coordinateSpace(name: "RefreshControl")
}
}
ScrollView の coordinateSpace(name: String) プロパティに名前を設定し RefreshControl 側で ScrollView を把握しますので、RefreshControl に渡す coordinateSpaceName と同じにしておかなければなりませんので注意してください。
以上
コメントを残す