パート 1: SwiftUI 2.0 と ストーリーボード
記事は3つの異なる部分に分かれている:
- パート 1: SwiftUI 2.0 と ストーリーボード (English)
- パート 2: SwiftUI 2.0 と UIKit
- パート 3: ストーリーボード と SwiftUI 2.0
パート 1は、SwiftUI2.0アプリケーションでのストーリーボードのビューの使用についてです。
SwiftUIビューからストーリーのビューコントローラーの使用
SwiftUIでストーリーボードビューコントローラーを使用できるようにするには、UIViewRepresentableおよびUIViewControllerRepresentableプロトコルに準拠するタイプでラップする必要があります。レガシーアプリの一部を再利用したり、ストーリーボードとUIKitで可能であるが、SwiftUIではまだ可能ではないことを実行する場合に役立ちます。
XCodeプロジェクト
新しい__SwiftUI__アプリプロジェクトを作成する
プロジェクトに名前を付ける
新しいストーリーボードのファイルを作成する
ストリーボードに名前を付ける
次に、デフォルトのUIViewController
にUI要素を挿入して、最初のビューコントローラーにする
これでストーリーボードを含むSwiftUIプロジェクトができましたが、SwiftUIプロジェクトのデフォルトの ContentView
はまだ表示されています。
それらを接続する必要があります...
StoryboardViewController:カスタムのUIViewController
のクラス
まず、カスタムの UIViewController
クラスをストーリーボードビューコントローラーに追加します...
UIViewController
のサブクラスである StoryboardViewController
クラスのSwiftファイルを作成します:
import SwiftUI
class StoryboardViewController: UIViewController {
}
次に、IBOutlet
とIBAction
を使用して、クラスをストーリーボードビューコントローラーのレイアウトのUI要素に接続します:
-
labelText
:2番目のLabel
要素に接続されたIBOutlet
。 -
inputText
:TextField
要素に接続されたIBOutlet
。 -
changeText(_:)
:「ChangeText!」ボタンのTouchUpInside
イベントに接続されたIBAction
。 -
goBack(_:)
:「GoBack!」ボタンのTouchUpInside
イベントに接続されたIBAction
。
class StoryboardViewController: UIViewController {
@IBOutlet weak var labelText: UILabel!
@IBOutlet weak var inputText: UITextField!
@IBAction func changeText(_ sender: Any) {
// ...
}
@IBAction func goBack(_ sender: Any) {
// ...
}
}
最後に、デリゲートプロトコル(次のセクションで定義されます)への参照を追加し、対応する IBAction
でそのメソッドを呼び出します:
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つのタイプを中で宣言します:
-
StoryboardViewControllerDelegate
:StoryboardViewController
に使用されるデリゲートプロトコル。 -
DefaultStoryboardViewControllerDelegate
:StoryboardViewControllerDelegate
のデフォルトの実装。メソッドが呼び出されても何も実行されません。
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つの要件があります:
-
UIViewController
を作成および設定するmakeUIViewController(context:)
メソッド - 指定されたビューコントローラーの状態をSwiftUIからの新しい情報で更新する
updateUIViewController(_:context:)
メソッド。この例では使用しないため、空白のままにします。
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」です)。
let storyboard = UIStoryboard(name: "Main", bundle: nil)
-
StoryboardViewController
を取得するには、instantiateInitialViewController()
メソッドを使用してストーリーボードの初期ビューコントローラーをインスタンス化するだけです。
let storyboardVC = storyboard.instantiateInitialViewController()
※ もう1つの方法は、ストーリーボードのビューコントローラーに__storyboardID__ ( "storyboardView"など)を設定し、ビューコントローラーの__storyboardID__を引数として使用するinstantiateViewController(withIdentifier:)
メソッドを呼び出すこと:
let storyboardVC = storyboard.instantiateViewController(withIdentifier: "storyboardView")
-
StoryboardView
のStoryboardViewController
のイベント通知を処理するデリゲートオブジェクトのCoordinator
を設定します:
storyboardVC.delegate = context.coordinator
※ 次のセクションでデリゲートオブジェクトのCoordinator
について説明します...
- そして最後に、
StoryboardViewController
を戻します:
return storyboardVC
Coordinator: StoryboardViewController
のデリゲート
ビューコントローラ内で発生する変更をSwiftUIインターフェイスの他の部分に伝達するには、カスタムコーディネータオブジェクトをSwiftUIビューに提供する必要があります。
ストリーボードのコンポーネントの変更を聞くために、デリゲートを使用する。デリゲートは、イベント自体がシステムによって処理される前または後にイベント通知を処理するために使用されます。
また、StoryboardViewController
の変更を聞くには、Coordinator
がStoryboardViewControllerDelegate
プロトコルに準拠する必要があります。また、NSObject
から継承して、Swiftで宣言できないNSObjectProtocol
プロトコルに準拠する必要があります。
変更が観察されるStoryboardView
インスタンスへの参照を設定するコンストラクターを使用してCoordinator
タイプを宣言しましょう:
struct StoryboardView: UIViewControllerRepresentable {
// ...
final class Coordinator: NSObject, StoryboardViewControllerDelegate {
var parent: StoryboardView
init(_ parent: StoryboardView) {
self.parent = parent
}
// ...
}
// ...
}
下記で、StoryboardViewControllerDelegate
に必要な2つのメソッドを実装します:
1.didPressChangeText(_:)
labelText
の内容を StoryboardViewController
の inputText
の内容に変更します:
func didPressChangeText(_ storyboardVC: StoryboardViewController) {
storyboardVC.labelText.text = storyboardVC.inputText.text
storyboardVC.inputText.text = ""
}
2.didPressGoBack(_:)
Coordinator
の親からpresentationMode
の環境プロパティを使用してビューを閉じるだけです:
func didPressGoBack(_ storyboardVC: StoryboardViewController) {
parent.presentationMode.wrappedValue.dismiss()
}
カスタムCoordinatorオブジェクト
提供されているデフォルトの実装の代わりに独自のコーディネーターオブジェクトを提供するには、StoryboardView
でmakeCoordinator()
メソッドを実装する必要があります:
struct StoryboardView: UIViewControllerRepresentable {
// ...
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
// ...
}
StoryboardViewのソースコード
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
を表示します:
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
で「▶」ボタンをクリックして、ストリーボードのビューコントローラーが表示される:
ストリーボードのビューで、テキストフィールドに新しい文章を入力する:
次に、「Change Text!」ボタンをクリックすると、2番目のText要素の「InitialText」が新しい文章に置き換えられる:
最後に、「Go Back!」ボタンをクリックすると、ストーリーボードビューを表示しているシートは却下されます:
やった!