LoginSignup
8

More than 3 years have passed since last update.

posted at

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

はじめに

この記事はジーズアカデミー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
What you can do with signing up
8