LoginSignup
0
1

More than 1 year has passed since last update.

SwiftUIからUIKitの部品を使う

Posted at

概要

SwiftUIからUIKitの部品を使うときの方法を紹介します。
サンプル用にシンプルな動作を考えました。
ボタンタップ時にラベルの文字列を変えるだけです。
動画にすると下のような感じです。

環境

Xcode 13.1
macOS Big Sur 11.6

手順

  1. UIViewRepresentableに準拠した構造体を作る
  2. UIKitから画面を作成するためにmakeUIViewを実装する
  3. 作成した画面が更新できるようにupdateUIViewを実装する
  4. UIKitからSwiftUIが保持している値を変更できるようにする

キーワードの解説

SwiftUIからUIKitを扱うにあたりキーワードがいくつかあります。
代表的なのはmakeUIView(context:), updateUIView(_:context:), Coordinatorです。
公式の説明を下に載せます。

makeUIView(context:)

ビューオブジェクトを作成し、その初期状態を構成します。

updateUIView(_:context:)

SwiftUIからの新しい情報で、指定されたビューの状態を更新します。

Coordinator

  1. システムは、ビュー内で発生した変更をSwiftUIインターフェースの他の部分に自動的に伝達しません。
  2. ビューを他のSwiftUIビューと調整する場合は、それらの相互作用を容易にするためにCoordinatorインスタンスを提供する必要があります。
  3. たとえば、コーディネーターを使用して、ターゲットアクションを転送し、ビューから任意のSwiftUIビューにメッセージを委任します。

実装

以下実装を進めます。

ボタンの初期状態を実装する

makeUIView(context:)UIButtonを返してあげます。

Views/CustomButton.swift
struct CustomButton: UIViewRepresentable {
    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 160, height: 44))
        button.setTitle("Custom Button", for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.addTarget(context.coordinator, action: #selector(Coordinator.didTapCustomButton(sender:)), for: .touchUpInside)
        return button
    }
}

ボタンのスタイルを更新するためのメソッドを追加する

中身は空ですがUIViewRepresentableプロトコルに準拠するために必須のメソッドです。

Views/CustomButton.swift
struct CustomButton: UIViewRepresentable {
...
    func updateUIView(_ uiView: UIButton, context: Context) {
    }
...
}

ボタンタップ時にラベルの文字列を変更できるようにCoordinatorクラスを実装する

名前解決にもつながるため、CustomButtonクラスの中でCoordinatorを宣言します。

Views/CustomButton.swift
struct CustomButton: UIViewRepresentable {
...
    func makeCoordinator() -> Coordinator {
        return Coordinator(button: self)
    }

    class Coordinator {
        var button: CustomButton

        init(button: CustomButton) {
            self.button = button
        }

        @objc func didTapCustomButton(sender: UIButton) {
            if button.text == "Default" {
                button.text = "Change"
                return
            }
            button.text = "Default"
        }
    }

...
}

最終的なコードは以下です。

Views/CustomView.swift
import SwiftUI

struct CustomButton: UIViewRepresentable {
    @Binding var text: String

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 160, height: 44))
        button.setTitle("Custom Button", for: .normal)
        button.setTitleColor(.black, for: .normal)
        button.addTarget(context.coordinator, action: #selector(Coordinator.didTapCustomButton(sender:)), for: .touchUpInside)
        return button
    }

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

    func makeCoordinator() -> Coordinator {
        return Coordinator(button: self)
    }

    class Coordinator {
        var button: CustomButton

        init(button: CustomButton) {
            self.button = button
        }

        @objc func didTapCustomButton(sender: UIButton) {
            if button.text == "Default" {
                button.text = "Change"
                return
            }
            button.text = "Default"
        }
    }
}

SwiftUIから呼び出す

UIKitの部品をSwiftUIから呼び出します。
記事では以下のようにしました。

Home.swift
import SwiftUI

struct Home: View {
    @State var text: String = "Default"

    var body: some View {
        VStack {
            Text(text)
            CustomButton(text: $text)
        }
    }
}

struct Home_Previews: PreviewProvider {
    static var previews: some View {
        Home()
    }
}

以上のコードで動画のような動作になると思います。

0
1
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
1