Swift UI 如何从子视图中检索状态
Swift UI how to retrieve state from child view
我有一个目前有三个视图的应用程序,一个 ParentView,它是一个用户可以添加条目的简单列表,一个 ContentView,它是一种模态形式,它收集信息以在 ParentView 中添加列表,以及一个ChildPickerView 重构了 ContentView 中的一些代码,这些代码得到了可怕的 'the compiler is unable to type-check this expression in reasonable time' 消息。
我的问题是 ChildPickerView 包含需要保存的文本。它随着 3 个选择器的变化而更新。当在 ContentView 中点击“保存”时,我试图将选择器返回的复合文本保存在 ChildPickerView 中。
我是 SwiftUI 的新手,所以可能遗漏了一些明显的东西,但这是我的代码。我在 ContentView 中尝试了 @State 对象,在 ChildPickerView 中尝试了 @Binding,但我的问题是构建复合文本视图,该视图跟踪我最终想要保存并显示在 ParentView 列表中的选择器的状态。
这是我的代码(为了简单起见,我只包含了 ContentView 和 ChildPickerView 的代码:
struct ContentView: View {
@State private var isCustomWidget: Bool = false
@State private var customWidgetName: String = ""
@State private var standardWidgetName: String = ""
var body: some View {
NavigationView {
Form {
Section(header: Text("WIDGET DETAILS")) {
Toggle(isOn: self.$isCustomWidget) {
Text("Custom")
}
if !self.isCustomWidget {
ChildPickerView(standardWidgetName: $standardWidgetName)
}
else
{
TextField("enter custom widget name", text: $customWidgetName)
}
}
Button(action: {
// we want to save either the standard widget name or custom widget name to our store
let str = self.isCustomWidget ? customWidgetName : standardWidgetName
print("saving \(str)")
})
{
Text("Save")
}
.navigationBarTitle("Add Widget")
}
}
}
}
struct ChildPickerView: View {
@State var selectedLengthIndex = 0
@State var selectedUnitsIndex = 0
@State var selectedItemIndex = 0
@Binding var standardWidgetName: String
var length: [String] = ["1","5","10","20","40","50","100","200"]
var units: [String] = ["Inches", "Centimeters"]
var items: [String] = ["Ribbon","Cord","Twine", "Rope"]
var body: some View {
List {
HStack {
Text("Widget Name")
Spacer()
let name =
"\(length[selectedLengthIndex])" + " " + " \(units[selectedUnitsIndex])" + " " + " \(items[selectedItemIndex])"
standardWidgetName = name //generates error on HStack -> Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols
Text(name)
}
Picker(selection: $selectedLengthIndex, label: Text("Length")) {
ForEach(0 ..< length.count) {
Text(self.length[[=12=]]).tag([=12=])
}
}
Picker(selection: $selectedUnitsIndex, label: Text("Unit of Measure")) {
ForEach(0 ..< units.count) {
Text(self.units[[=12=]]).tag([=12=])
}
}
Picker(selection: $selectedItemIndex, label: Text("Item Type")) {
ForEach(0 ..< items.count) {
Text(self.items[[=12=]]).tag([=12=])
}
}
}
}
}
在 SwiftUI 中,您需要数据的单一真实来源,然后所有视图都读取和写入该数据。
@State
有点像 private,因为它创建了一个新的事实来源,只能由该视图(及其子视图)使用。
您想要的是将数据放在父级(本例中为 ContentView)中,然后将您希望子级更新的数据传递给它们。
您可以使用 @Binding
:
public struct ChildPickerView : View {
var selectedText: Binding<String>
var body: some View { }
}
然后使用 $
:
从父级传递该绑定
public struct ContentView : View {
@State var selectedText = ""
var body: some View {
ChildViewItem($selectedText)
}
}
您还可以使用 EnvironmentObject
来避免在视图之间传递数据。
我有一个目前有三个视图的应用程序,一个 ParentView,它是一个用户可以添加条目的简单列表,一个 ContentView,它是一种模态形式,它收集信息以在 ParentView 中添加列表,以及一个ChildPickerView 重构了 ContentView 中的一些代码,这些代码得到了可怕的 'the compiler is unable to type-check this expression in reasonable time' 消息。
我的问题是 ChildPickerView 包含需要保存的文本。它随着 3 个选择器的变化而更新。当在 ContentView 中点击“保存”时,我试图将选择器返回的复合文本保存在 ChildPickerView 中。
我是 SwiftUI 的新手,所以可能遗漏了一些明显的东西,但这是我的代码。我在 ContentView 中尝试了 @State 对象,在 ChildPickerView 中尝试了 @Binding,但我的问题是构建复合文本视图,该视图跟踪我最终想要保存并显示在 ParentView 列表中的选择器的状态。
这是我的代码(为了简单起见,我只包含了 ContentView 和 ChildPickerView 的代码:
struct ContentView: View {
@State private var isCustomWidget: Bool = false
@State private var customWidgetName: String = ""
@State private var standardWidgetName: String = ""
var body: some View {
NavigationView {
Form {
Section(header: Text("WIDGET DETAILS")) {
Toggle(isOn: self.$isCustomWidget) {
Text("Custom")
}
if !self.isCustomWidget {
ChildPickerView(standardWidgetName: $standardWidgetName)
}
else
{
TextField("enter custom widget name", text: $customWidgetName)
}
}
Button(action: {
// we want to save either the standard widget name or custom widget name to our store
let str = self.isCustomWidget ? customWidgetName : standardWidgetName
print("saving \(str)")
})
{
Text("Save")
}
.navigationBarTitle("Add Widget")
}
}
}
}
struct ChildPickerView: View {
@State var selectedLengthIndex = 0
@State var selectedUnitsIndex = 0
@State var selectedItemIndex = 0
@Binding var standardWidgetName: String
var length: [String] = ["1","5","10","20","40","50","100","200"]
var units: [String] = ["Inches", "Centimeters"]
var items: [String] = ["Ribbon","Cord","Twine", "Rope"]
var body: some View {
List {
HStack {
Text("Widget Name")
Spacer()
let name =
"\(length[selectedLengthIndex])" + " " + " \(units[selectedUnitsIndex])" + " " + " \(items[selectedItemIndex])"
standardWidgetName = name //generates error on HStack -> Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols
Text(name)
}
Picker(selection: $selectedLengthIndex, label: Text("Length")) {
ForEach(0 ..< length.count) {
Text(self.length[[=12=]]).tag([=12=])
}
}
Picker(selection: $selectedUnitsIndex, label: Text("Unit of Measure")) {
ForEach(0 ..< units.count) {
Text(self.units[[=12=]]).tag([=12=])
}
}
Picker(selection: $selectedItemIndex, label: Text("Item Type")) {
ForEach(0 ..< items.count) {
Text(self.items[[=12=]]).tag([=12=])
}
}
}
}
}
在 SwiftUI 中,您需要数据的单一真实来源,然后所有视图都读取和写入该数据。
@State
有点像 private,因为它创建了一个新的事实来源,只能由该视图(及其子视图)使用。
您想要的是将数据放在父级(本例中为 ContentView)中,然后将您希望子级更新的数据传递给它们。
您可以使用 @Binding
:
public struct ChildPickerView : View {
var selectedText: Binding<String>
var body: some View { }
}
然后使用 $
:
public struct ContentView : View {
@State var selectedText = ""
var body: some View {
ChildViewItem($selectedText)
}
}
您还可以使用 EnvironmentObject
来避免在视图之间传递数据。