質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

3818閲覧

SwiftUIにて、一つの状態変数を複数のビューで使用する方法

yut0124

総合スコア6

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2019/09/16 05:28

実現したいこと

あるビューにテキストフィールドを設置して、そこにユーザが入力した文字列を変数に格納し、他のビューでその変数を用いることで表示するようにしたいのですが、以下の問題があり実現できません。

・TextField(text:)のtext:の部分は、@Stateをつけた状態変数しか対応していないため、単純な文字列として取り出すことができません。
・テキストフィールドのあるビューの中でテキストフィールドの入力内容を格納する@State変数を宣言して使用すると、入力から取り出しまで成功します。
・他のビューで使用するために、この変数を別のクラスや構造体の中に置き、テキストフィールドのあるビューでインスタンス化して使用しようとすると、ビルドはできるものの実行するとクラッシュします。

以上から、テキストフィールドへの入力内容を取り出せるのは、そのビューだけという状況になり、やりたいことができません。

@EnvironmentObjectを用いると複数のビューで状態変数を共有できるという方法もあるようですが、その詳しいやり方もよく分かっていません。
SwiftUIで状態変数を複数のビューで扱う分かりやすい方法が知りたいです。

SwiftUIチュートリアルはすでに目を通しています。

発生している問題・エラーメッセージ

ビルドは成功しますが,実行するとクラッシュします。
Cannot preview in this file - ***.app may have crashed.

試したコード

Swift

1import SwiftUI 2 3struct TextFieldView: View { 4 @State var data: String 5 var body: some View { 6 VStack { 7 TextField(text: $data) 8 Text("(data)") 9 } 10 } 11}

Swift

1import SwiftUI 2 3struct Data { 4 @State var name = "" 5} 6 7struct TextFieldView: View { 8 var data: Data 9 var body: some View { 10 VStack { 11 TextField(text: $data.name) //ビルドに成功するがクラッシュする 12 Text("(data.name)") 13 } 14 } 15}

補足

Xcodeは最新のGM版を用いています。
初学者ですがよろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

・他のビューで使用するために、この変数を別のクラスや構造体の中に置き、テキストフィールドのあるビューでインスタンス化して使用しようとすると、ビルドはできるものの実行するとクラッシュします。

@State はビューの中でのみ宣言できます。
ビューの外で使用する場合は ObservableObject と @Published を使用します。

Swift

1import SwiftUI 2 3class ViewModel: ObservableObject { 4 @Published var name: String = "" 5} 6// ObservableObject は AnyObject を継承するので class でのみ使用可能です 7// また @Published を付けた変数を変更すると、参照するすべての箇所に変更が伝播されます 8 9struct TextFieldView: View { 10 @ObservedObject var vm = ViewModel() 11 // ObservableObject を利用する側は @ObservedObject をつける 12 13 var body: some View { 14 VStack { 15 TextField("Placeholder", text: $vm.name) 16 Text("(vm.name)") 17 } 18 } 19}

@EnvironmentObjectを用いると複数のビューで状態変数を共有できるという方法もあるようですが、その詳しいやり方もよく分かっていません。

共有するには @State を @Binding するか ObservableObject をバケツリレーのように引き渡すことで行えます。

Swift

1// Binding の例 2import SwiftUI 3 4struct FirstView: View { 5 @State var name: String = "" 6 7 var body: some View { 8 VStack { 9 SecondView(name: $name) // Binding へ渡す時は $ をつける 10 Text("(name)") 11 } 12 } 13} 14 15struct SecondView: View { 16 @Binding var name: String 17 18 var body: some View { 19 VStack { 20 TextField("Placeholder", text: $name) 21 ThirdView(name: $name) 22 } 23 } 24} 25 26 27struct ThirdView: View { 28 @Binding var name: String 29 30 var body: some View { 31 Text(name) 32 } 33}

Swift

1// ObservableObject の例 2import SwiftUI 3 4class ViewModel: ObservableObject { 5 @Published var name = "" 6} 7 8struct FirstView: View { 9 @ObservedObject var vm = ViewModel() // インスタンスを生成 10 11 var body: some View { 12 VStack { 13 SecondView(vm: vm) // $ は不要 14 Text("(vm.name)") 15 } 16 } 17} 18 19struct SecondView: View { 20 @ObservedObject var vm: ViewModel 21 22 var body: some View { 23 VStack { 24 TextField("Placeholder", text: $vm.name) 25 ThirdView(vm: vm) 26 } 27 } 28} 29 30struct ThirdView: View { 31 @ObservedObject var vm: ViewModel 32 33 var body: some View { 34 Text(vm.name) 35 } 36}

バケツリレー方式だと、ビューの階層が深いと記述が煩雑になります。
この場合に @EnvironmentObject を使用します。

Swift

1import SwiftUI 2 3class ViewModel: ObservableObject { 4 @Published var name = "" 5} 6 7struct FirstView: View { 8 @ObservedObject var vm = ViewModel() // インスタンスを生成 9 10 var body: some View { 11 VStack { 12 SecondView() 13 .environmentObject(vm) 14 // SecondView 以下に @EnvironmentObject を設定 15 16 Text("(vm.name)") 17 } 18 } 19} 20 21struct SecondView: View { 22 @EnvironmentObject var vm: ViewModel 23 24 var body: some View { 25 VStack { 26 TextField("Placeholder", text: $vm.name) 27 ThirdView() 28 } 29 } 30} 31 32struct ThirdView: View { 33 var body: some View { 34 VStack { 35 Text("hoge hoge") 36 FourthView() 37 } 38 } 39 // 共有された変数を使わないビューは @EnvironmentObject の記述を省略可能 40} 41 42struct FourthView: View { 43 @EnvironmentObject var vm: ViewModel 44 45 var body: some View { 46 Text(vm.name) 47 } 48}

とりあえずは @State を @Binding して参照し、ビューの階層が増えてから ObservableObject や @EnvironmentObject の使用を検討すれば大丈夫です。

投稿2019/09/19 03:05

編集2019/09/19 03:09
kechizenya

総合スコア34

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yut0124

2019/09/20 07:11

ありがとうございます! 設計通りの実装ができました
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問