はじめに
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側双方向のイベントを処理できるようになりました。活用の幅が広がりそうですね。



