0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftUI 2.0と UIKit と ストーリーボード (パート 1)

Last updated at Posted at 2021-05-20

パート 1: SwiftUI 2.0 と ストーリーボード

記事は3つの異なる部分に分かれている:

パート 1は、SwiftUI2.0アプリケーションでのストーリーボードのビューの使用についてです。

SwiftUIビューからストーリーのビューコントローラーの使用

SwiftUIでストーリーボードビューコントローラーを使用できるようにするには、UIViewRepresentableおよびUIViewControllerRepresentableプロトコルに準拠するタイプでラップする必要があります。レガシーアプリの一部を再利用したり、ストーリーボードとUIKitで可能であるが、SwiftUIではまだ可能ではないことを実行する場合に役立ちます。

XCodeプロジェクト

新しい__SwiftUI__アプリプロジェクトを作成する
image.png
プロジェクトに名前を付ける
Screen Shot 2021-05-19 at 15.40.59.png
新しいストーリーボードのファイルを作成する
image.png
ストリーボードに名前を付ける
Screen Shot 2021-05-19 at 15.45.27.png
次に、デフォルトのUIViewControllerにUI要素を挿入して、最初のビューコントローラーにする
Screen Shot 2021-05-19 at 22.50.07.png
これでストーリーボードを含むSwiftUIプロジェクトができましたが、SwiftUIプロジェクトのデフォルトの ContentViewはまだ表示されています。
それらを接続する必要があります...

StoryboardViewController:カスタムのUIViewControllerのクラス

まず、カスタムの UIViewControllerクラスをストーリーボードビューコントローラーに追加します...

UIViewControllerのサブクラスである StoryboardViewControllerクラスのSwiftファイルを作成します:

StoryboardViewController.swift
import SwiftUI

class StoryboardViewController: UIViewController {
}

次に、IBOutletIBActionを使用して、クラスをストーリーボードビューコントローラーのレイアウトのUI要素に接続します:

  • labelText:2番目のLabel要素に接続されたIBOutlet
  • inputTextTextField要素に接続されたIBOutlet
  • changeText(_:):「ChangeText!」ボタンのTouchUpInsideイベントに接続されたIBAction
  • goBack(_:):「GoBack!」ボタンのTouchUpInsideイベントに接続されたIBAction
StoryboardViewController.swift
class StoryboardViewController: UIViewController {
    @IBOutlet weak var labelText: UILabel!
    @IBOutlet weak var inputText: UITextField!
    @IBAction func changeText(_ sender: Any) {
        // ...
    }
    @IBAction func goBack(_ sender: Any) {
        // ...
    }
}

最後に、デリゲートプロトコル(次のセクションで定義されます)への参照を追加し、対応する IBActionでそのメソッドを呼び出します:

StoryboardViewController.swift
import SwiftUI

class StoryboardViewController: UIViewController {
    var delegate: StoryboardViewControllerDelegate = DefaultStoryboardViewControllerDelegate()
    @IBOutlet weak var labelText: UILabel!
    @IBOutlet weak var inputText: UITextField!
    @IBAction func changeText(_ sender: Any) {
        delegate.didPressChangeText(self)
    }
    @IBAction func goBack(_ sender: Any) {
        delegate.didPressGoBack(self)
    }
}

StoryboardViewControllerDelegate

それでは、ストーリーボードビューコントローラー内で発生する変更をSwiftUIインターフェイスの他の部分に伝達するために使用されるデリゲートプロトコルを作成しましょう...

Swiftファイルを作成して、下記の2つのタイプを中で宣言します:

  • StoryboardViewControllerDelegateStoryboardViewControllerに使用されるデリゲートプロトコル。
  • DefaultStoryboardViewControllerDelegateStoryboardViewControllerDelegateのデフォルトの実装。メソッドが呼び出されても何も実行されません。
StoryboardViewControllerDelegate.swift
import Foundation

protocol StoryboardViewControllerDelegate: AnyObject {
    func didPressChangeText(_ storyboardVC: StoryboardViewController)
    func didPressGoBack(_ storyboardVC: StoryboardViewController)
}

class DefaultStoryboardViewControllerDelegate: StoryboardViewControllerDelegate {
    func didPressChangeText(_ storyboardVC: StoryboardViewController) {}
    func didPressGoBack(_ storyboardVC: StoryboardViewController) {}
}

StoryboardViewControllerDelegateプロトコルには2つのメソッドがあります:

  • didPressChangeText(_:):ユーザーが「ChangeText!」ボタンを押したときに呼び出されるメソッド。
  • didPressGoBack(_:):ユーザーが「GoBack!」ボタンを押したときに呼び出されるメソッド。

簡単にするために、両方のメソッドには1つのパラメーターしかありません。変更が監視されるStoryboardViewControllerインスタンスです。

StoryboardView:UIViewControllerRepresentableに準拠するタイプ

次に、StoryboardViewControllerをラップするStoryboardViewタイプを作成しましょう。

UIViewControllerRepresentableプロトコルには、次の2つの要件があります:

StoryboardView.swift
import SwiftUI

struct StoryboardView: UIViewControllerRepresentable {
    func makeUIViewController(context: UIViewControllerRepresentableContext<StoryboardView>) -> StoryboardViewController {
        // ...
    }
    func updateUIViewController(_ uiViewController: StoryboardViewController, context: UIViewControllerRepresentableContext<StoryboardView>) {
    }
}

次に、makeUIViewController(context:)メソッドを実装して、ストーリーボードからStoryboardViewControllerのインスタンスを戻します。 SwiftUIによって呼び出されたとき。 SwiftUIは、ビューを表示する準備ができたときにこのメソッドを1度呼び出し、その後、ビューコントローラーのライフサイクルを管理します。

これを行うには、ストーリーボードからStoryboardViewControllerを取得する必要があります:

  • ストーリーボードを取得するには、ストーリーボードファイルの名前でUIStoryboardオブジェクトをインスタンス化します(ここでは、ファイルの名前が「Main.storyboard」であるため、ストーリーボードの名前は「Main」です)。
Swift
let storyboard = UIStoryboard(name: "Main", bundle: nil)
  • StoryboardViewControllerを取得するには、instantiateInitialViewController()メソッドを使用してストーリーボードの初期ビューコントローラーをインスタンス化するだけです。
Swift
let storyboardVC = storyboard.instantiateInitialViewController()

※ もう1つの方法は、ストーリーボードのビューコントローラーに__storyboardID__ ( "storyboardView"など)を設定し、ビューコントローラーの__storyboardID__を引数として使用するinstantiateViewController(withIdentifier:)メソッドを呼び出すこと:

Swift
let storyboardVC = storyboard.instantiateViewController(withIdentifier: "storyboardView")
  • StoryboardViewStoryboardViewControllerのイベント通知を処理するデリゲートオブジェクトのCoordinatorを設定します:
Swift
    storyboardVC.delegate = context.coordinator

※ 次のセクションでデリゲートオブジェクトのCoordinatorについて説明します...

  • そして最後に、StoryboardViewControllerを戻します:
Swift
    return storyboardVC

Coordinator: StoryboardViewControllerのデリゲート

ビューコントローラ内で発生する変更をSwiftUIインターフェイスの他の部分に伝達するには、カスタムコーディネータオブジェクトをSwiftUIビューに提供する必要があります。

ストリーボードのコンポーネントの変更を聞くために、デリゲートを使用する。デリゲートは、イベント自体がシステムによって処理される前または後にイベント通知を処理するために使用されます。

また、StoryboardViewControllerの変更を聞くには、CoordinatorStoryboardViewControllerDelegateプロトコルに準拠する必要があります。また、NSObjectから継承して、Swiftで宣言できないNSObjectProtocolプロトコルに準拠する必要があります。

変更が観察されるStoryboardViewインスタンスへの参照を設定するコンストラクターを使用してCoordinatorタイプを宣言しましょう:

Swift
struct StoryboardView: UIViewControllerRepresentable {
    // ...
    final class Coordinator: NSObject, StoryboardViewControllerDelegate {
        var parent: StoryboardView
        init(_ parent: StoryboardView) {
            self.parent = parent
        }
        // ...
    }
    // ...
}

下記で、StoryboardViewControllerDelegateに必要な2つのメソッドを実装します:

1.didPressChangeText(_:)

labelTextの内容を StoryboardViewControllerinputTextの内容に変更します:

Swift
    func didPressChangeText(_ storyboardVC: StoryboardViewController) {
        storyboardVC.labelText.text = storyboardVC.inputText.text
        storyboardVC.inputText.text = ""
    }
2.didPressGoBack(_:)

Coordinatorの親からpresentationModeの環境プロパティを使用してビューを閉じるだけです:

Swift
    func didPressGoBack(_ storyboardVC: StoryboardViewController) {
        parent.presentationMode.wrappedValue.dismiss()
    }
カスタムCoordinatorオブジェクト

提供されているデフォルトの実装の代わりに独自のコーディネーターオブジェクトを提供するには、StoryboardViewmakeCoordinator()メソッドを実装する必要があります:

Swift
struct StoryboardView: UIViewControllerRepresentable {
    // ...
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    // ...
}

StoryboardViewのソースコード

StoryboardView.swift
import SwiftUI

struct StoryboardView: UIViewControllerRepresentable {
    @Environment(\.presentationMode) private var presentationMode
    final class Coordinator: NSObject, StoryboardViewControllerDelegate {
        var parent: StoryboardView
        init(_ parent: StoryboardView) {
            self.parent = parent
        }
        func didPressChangeText(_ storyboardVC: StoryboardViewController) {
            storyboardVC.labelText.text = storyboardVC.inputText.text
            storyboardVC.inputText.text = ""
        }
        func didPressGoBack(_ storyboardVC: StoryboardViewController) {
            parent.presentationMode.wrappedValue.dismiss()
        }
    }
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    func makeUIViewController(context: UIViewControllerRepresentableContext<StoryboardView>) -> StoryboardViewController {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let storyboardVC = storyboard.instantiateInitialViewController() as! StoryboardViewController
        storyboardVC.delegate = context.coordinator
        return storyboardVC
    }
    func updateUIViewController(_ uiViewController: StoryboardViewController, context: UIViewControllerRepresentableContext<StoryboardView>) {
    }
}

ContentViewのソースコード

それでは、 StoryboardViewをアプリのContentViewに追加しましょう。

Buttonを使用し、sheet(isPresented:onDismiss:content:)メソッドを使用してシートでStoryboardViewを表示します:

ContentView.swift
import Foundation
import UIKit
import SwiftUI

struct ContentView: View {
    @State private var isShowingStoryboardView = false

    var body: some View {
        VStack {
        Text("Launch Storyboard View!")
            .padding()
            Button(action: {
                self.isShowingStoryboardView = true
            }) {
                Image(systemName: "play.fill")
                    .font(.system(size: 40))
            }.sheet(isPresented: self.$isShowingStoryboardView) {
                StoryboardView()
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

アプリ

アプリを起動し、ContentViewで「▶」ボタンをクリックして、ストリーボードのビューコントローラーが表示される:
image.png
ストリーボードのビューで、テキストフィールドに新しい文章を入力する:
image.png
次に、「Change Text!」ボタンをクリックすると、2番目のText要素の「InitialText」が新しい文章に置き換えられる:
image.png
最後に、「Go Back!」ボタンをクリックすると、ストーリーボードビューを表示しているシートは却下されます:
image.png
やった!

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?