LoginSignup
9

More than 3 years have passed since last update.

[SwiftUI]sheetで遷移するView間で値を引き渡す

Posted at

はじめに

この記事はジーズアカデミーAdvent Calendar 2019 3日目の記事です。
こんにちは!Dev(週末コース)8期の@Ryu0823といいます。
ジーズではWebコースで、最後のDemo dayも参加できなかったのですが、卒業してからもいろいろな言語をかじり、最近はSwiftUIでアプリを制作しています。
SwiftUIはSwiftより簡単かつわかりやすいと感じていますが、まだ新しい言語のためできないこともあったり、情報も少ないのが現状です。
そこで今回は、sheetで遷移する場合にView間で値を渡す方法を探すのに苦労したので、備忘録的に書いてみます。
完成形は↓
Nov-24-2019 21-50-32.gif

やることと初期状態

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はまだ出来たばかりなので、ドキュメントが未整備な部分もあるのですが、このサイトではかなり詳しく解説してくれています。

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
9