LoginSignup
0
2

Coordinatorクラスについて簡単にアウトプット

Last updated at Posted at 2023-09-29

Coordinatorとは?

  • UIKitでのイベントをSwiftUIで管理する(取得するなど)ために作成するクラスのこと。

    • 先述した通り、主な役割としては、UIKitのイベントやデータの受け渡しをSwiftUIに伝える。
      • (ラップされた)UIKitのViewのdelegatetarget actionをハンドリングしたい場合、structではハンドリングできないため、Coordinatorクラスという仕組みが採用されている。
  • Coordinatorクラスはデフォルトで存在しないため、独自で実装する必要がある。一般的にはCoordinatorという命名である。

主なメソッド

makeCoordinatorメソッド

  • makeCoordinatorメソッドはmakeUIViewメソッドよりも先に呼ばれるので、そこ(makeCoordinatorメソッド内)でCoordinatorを作成する。

  • その後、makeUIViewメソッドやupdateUIViewメソッドなどで渡されるcontextにcoordinatorが含まれる。そのcoordinatorを用いればUIKitで作成したViewとSwiftUI側でやり取り(イベント管理など)ができる。

ソースコード(いくつかのパターンを添えて)

  • イベントの受け渡し
Swift
// (SwiftUIで使えるようにラップされた)UIKitのUIButtonがタップされたときに、そのイベントをSwiftUIのViewに受け渡し(通知し)、SwiftUI側でラベルにメッセージを表示する
struct ButtonView: UIViewRepresentable {
    // UIViewRepresentable使えば、Bindingも可能になる
    @Binding var message: String

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("ボタン", for: .normal)
        // coordinatorがSwiftUIとUIKitの仲介役(イベント管理的役割)となってイベントを受け渡すことができる
        button.addTarget(context.coordinator, action: #selector(Coordinator.tappedButton), for: .touchUpInside)
        return button
    }

    func updateUIView(_ uiView: UIButton, context: Context) {
    }

    func makeCoordinator() -> Coordinator {
        // 仲介対象はこのButtonView自身
        Coordinator(self)
    }
}

// SwiftUIとUIKitの仲介役
// classなのでイベント管理やdelegateが可能
class Coordinator: NSObject {
    var parent: ButtonView

    init(_ parent: ButtonView) {
        self.parent = parent
    }

    @objc func tappedButton() {
        // UIKitのボタンがタップされたときに呼び出される
        // メッセージをSwiftUIに伝える
        parent.message = "「ボタンが押された」というメッセージに変わりました"
    }
}

// messageプロパティのStringをボタンタップによって変化させ、SwiftUI側に表示させる
struct ContentView: View {
    @State var message = "ボタンはまだ押されていません"

    var body: some View {
        VStack {
            Text(message)
            ButtonView(message: $message)
        }
    }
}
  • delegateを噛ませてイベントの受け渡し
    • ただし、わざわざdelegateを生やして処理を委譲する(データを渡す)のではなく、構造体に処理委譲を任せるクロージャーを引数として生やして、インスタンス化先で処理を任せるみたいな対応が好ましいかもしれない。
Swift
protocol ButtonDelegate {
    func TappedButton()
}

struct ButtonView: UIViewRepresentable {
    var delegate: ButtonDelegate

    // UIKitの初期状態のインスタンスを作成
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle("ボタン", for: .normal)
        button.addTarget(context.coordinator, action: #selector(Coordinator.tappedButton), for: .touchUpInside)
        return button
    }

    func updateUIView(_ uiView: UIButton, context: Context) {
    }

    // SwiftUIとUIKitの仲介(やりとり)
    // UIKitのdelegate通知をSwiftUIで受け取れるようにしている
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

// delegateを準拠
// この構造体に処理を委譲する
struct ButtonDelegateImpl: ButtonDelegate {
    @Binding var message: String

    func TappedButton() {
        message = "ボタンがタップされました"
    }
}

// SwiftUIとUIKitの仲介(やりとり)
// UIKitのdelegate通知をSwiftUIで受け取れるようにする
class Coordinator: NSObject {
    let parent: ButtonView

    init(_ parent: ButtonView) {
        self.parent = parent
    }

    @objc func tappedButton() {
        // UIKitのボタンが押されるとdelegate経由で通知
        parent.delegate.TappedButton()
    }
}

struct ContentView: View {
    @State var message = ""

    var body: some View {
        VStack {
            Text(message)
            // ButtonViewの引数に(処理をお願いする)ButtonDelegateImplを持たせる
            ButtonView(delegate: ButtonDelegateImpl(message: $message))
        }
    }
}

おわりに

間違い等ありましたらコメント欄にてご指摘ください!

参考記事

開発環境

  • Xcode-14.3
  • Swift version 5.8
0
2
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
0
2