はじめに
UIListViewControllerを引っ張ってリロードする仕組みをSwiftUIでやってみました。
リロード処理はボタンのタップで走らせるのが楽ちんです。しかしながら、UI的には引っ張ってリロードのほうが良さげ(個人的主観)なので、試してみたいと思います。
UIScrollViewのビューをラップする
makeUIViewメソッドを作る
非同期通信中にActivityIndicatorを表示させてみる時と同様に、UIViewRepresentable
プロトコルに準拠したSwiftUIのビューを作ります。ラップするのはUIScrollView
です。
func makeUIView(context: Context) -> UIScrollView {
let control = UIScrollView()
return control
}
ただこれでは引っ張ってリロードができないので、UIRefreshControl
をrefreshControl
プロパティに代入しておきます。また、valueChanged
イベントが起こったときにリロード処理が実行されるようaddTarget
しておきます。
func makeUIView(context: Context) -> UIScrollView {
let control = UIScrollView()
control.refreshControl = UIRefreshControl()
control.refreshControl?.addTarget(context.coordinator, action:nil, for: .valueChanged)
return control
}
context.coordinator
という見慣れないものがありますが、後ほど。
Coordinatorクラス登場!
UIRefreshControl
のイベントをSwiftUIへ伝えるためにCoordinator
クラスを実装します。この点が非同期通信中にActivityIndicatorを表示させてみる時と違うところです。
このCoordinator
クラスはmakeUIView
メソッド実行時に、makeCoordinator
メソッドにて1度だけ生成されます。
Coordinatorクラスの実装
Coordinator
クラスのスッピンバージョンがこちらです。
class Coordinator: NSObject {
var view: [ラップされたビュー]
init(_ view: [ラップされたビュー]) {
self.view = view
}
}
コーディネート対象のビューはRefreshView
です。そして、UIRefreshControl
のhandleRefreshControl
メソッドを実行したいので、このクラス内に実装します。
class Coordinator: NSObject {
var view: RefreshView
init(_ view: RefreshView) {
self.view = view
}
@objc func handleRefreshControl(sender: UIRefreshControl) {
sender.endRefreshing()
}
}
CoordinatorクラスをmakeCoordinatorメソッドでRefreshViewに保持させる
保持させるという表現が合っているのかどうかはちょっと不安。ここはあえて「保持する」という表現で行きます。
Coordinator
クラスをRefreshView
で保持するためにmakeCoordinator
メソッドを実装します。
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
非常にシンプル。UIViewRepresentable
プロトコルに準拠させておけば、makeCoordinator
メソッドはmakeUIView
メソッド実行時に自動で1回だけ呼ばれます。やるべきことはこれだけです。
ラップしたビューでイベントが実行されるようにする
仕上げです。Coordinator
クラスで実装したメソッドをラップしたビューで実行されるようにします。makeUIView
メソッドにて記述したaddTarget
に呼び出すメソッドを記述します。
control.refreshControl?.addTarget(context.coordinator, action:#selector(Coordinator.handleRefreshControl), for: .valueChanged)
ここでcontext.coordinator
ですよ!いつものcontext
は文脈的なあれです。文脈的な流れ上、context
のcoordinator
プロパティにはmakeCoordinator
メソッドで生成したCoordinator
クラスが入っています(入っているはずです)。
まとめ
思ったよりもあっさりできました。引っ張ったら一瞬インジケータ(くるくる)が表示されます。
ラップしたビューはUIScrollView
なので、ここまでで用意したビューに別のビューを載せて、handleRefreshControl
メソッドにリロード時の処理を追加すればOKです。
例えば、List
ビューを載せて、handleRefreshControl
メソッドにDBからのデータ取得処理を実行するなどです。ちなみに、List
ビューでデータ取得処理を監視しておいて、データが流れてきたらリストに表示するのは非同期通信中にActivityIndicatorを表示させてみる時の応用でできそうです。
SwiftUI側とラップされるUIKit側双方向のイベントを処理できるようになりました。活用の幅が広がりそうですね。