はじめに
この記事はジーズアカデミーAdvent Calendar 2019 3日目の記事です。
こんにちは!Dev(週末コース)8期の@Ryu0823といいます。
ジーズではWebコースで、最後のDemo dayも参加できなかったのですが、卒業してからもいろいろな言語をかじり、最近はSwiftUIでアプリを制作しています。
SwiftUIはSwiftより簡単かつわかりやすいと感じていますが、まだ新しい言語のためできないこともあったり、情報も少ないのが現状です。
そこで今回は、sheetで遷移する場合にView間で値を渡す方法を探すのに苦労したので、備忘録的に書いてみます。
完成形は↓
やることと初期状態
FirstViewからsheetとしてSecondViewを表示し、SecondViewで入力された値をFirstViewに表示します。
struct FirstView: View {
var body: some View {
VStack {
Text("Hello") // <-SecondViewで入力された値を表示する
Button(action: {
print("Button tapped.") // <-SecondViewへ遷移する
}) {
Text("Button")
}
}
}
}
struct SecondView: View {
@State var userName = ""
var body: some View {
NavigationView {
Form {
Section(header: Text("What is your name?")) {
TextField("Name", text: $userName) // <-ここで入力された値をFirstViewへ戻す
}
}
.navigationBarTitle("SecondView", displayMode: .inline)
// FirstViewへ戻る処理が必要
}
}
}
FirstViewからsheetとしてSecondViewを表示
struct FirstView: View {
// SecondViewの表示/非表示のState
@State var showSecondView = false
var body: some View {
VStack {
Text("Hello") // <-SecondViewで入力された値を表示する
Button(action: {
// SecondViewの表示/非表示の切り替え
self.showSecondView.toggle()
}) {
Text("Tell your name")
}
}
.sheet(isPresented: self.$showSecondView) {
// SecondViewを表示
SecondView(isPresent: self.$showSecondView)
}
}
}
}
struct SecondView: View {
@State var userName = ""
// SecondViewの表示/非表示
@Binding var isPresent: Bool
var body: some View {
NavigationView {
Form {
Section(header: Text("What is your name?")) {
TextField("Name", text: $userName) // <-ここで入力された値をFirstViewへ戻す
}
}
.navigationBarTitle("SecondView", displayMode: .inline)
// FirstViewへ戻る
.navigationBarItems(trailing:
Button(action: {
self.isPresent = false
}) {
Text("Done")
}
)
}
}
}
View間で値を連携する
ObservableObjectを定義
View間で連携したい変数を、ObservableObject classを継承したclassのpropertyとして定義
final class ViewModel: ObservableObject {
@Published var name = ""
}
FirstViewにObservedObjectを追加
struct FirstView: View {
@State var showSecondView = false
// ViewModelをObservedObjectとして初期化
@ObservedObject var userName = ViewModel()
var body: some View {
VStack {
// ObservedObjectのname propertyを表示(SecondViewの値を表示したい)
Text("Hello \(self.userName.name)")
Button(action: {
self.showSecondView.toggle()
}) {
Text("Tell your name")
}
}
.sheet(isPresented: self.$showSecondView) {
// SecondViewを表示
SecondView(isPresent: self.$showSecondView)
}
}
}
}
FirstViewで初期化したObservedObjectをSecondViewと共有する
SecondViewではFirstViewと同じObservedObjectを見る必要があります。
そのため、SecondViewではViewModel classを指定するだけで、初期化はしません。
初期化すると別のObservedObjectが作成され、View間で値の共有ができません。
(私はここでつまった💦)
struct SecondView: View {
@Binding var isPresent: Bool
// FirstViewから呼び出す際にFirstViewで初期化したViewModel変数を指定
@ObservedObject var userName: ViewModel
var body: some View {
NavigationView {
Form {
Section(header: Text("What is your name?")) {
TextField("Name", text: $userName.name)
}
}
.navigationBarTitle("SecondView", displayMode: .inline)
.navigationBarItems(trailing:
Button(action: {
self.isPresent = false
}) {
Text("Done")
}
)
}
}
}
最後にFirstView内のSecondView初期化を修正
SecondViewで定義したuserName propertyにFirstViewのuserNameが入ります。
これでSecondViewのTextFieldに入力した値がFirstViewに連携されます。
SecondView(isPresent: self.$showSecondView, userName: self.userName)
終わりに & SwiftUI参考リンク
まだまだ勉強中ですが、この記事が少しでも役に立てば幸いです!
SwiftUIやSwiftそのほかモバイル向け言語やってる方いたらお気軽にSNSで繋がりましょう!
最後にSwiftUI学習の参考サイトを載せておきます。
・100 days of SwiftUI - Hacking with Swift
#100daysOfSwiftUIでTwitterに毎日勉強したことを投稿してます。
・The SwiftUI Lab
SwiftUIはまだ出来たばかりなので、ドキュメントが未整備な部分もあるのですが、このサイトではかなり詳しく解説してくれています。