Swift @State变量的值不会更改
我创建了一个视图,它提供了一个方便的保存按钮和保存方法。然后可以在父视图中使用这两种视图。 我们的想法是提供这些功能,以便可以自定义导航栏项,但保留原始实现 在视图中有一个绑定到@State变量的Textfield。如果从同一视图中调用save方法,则一切都会按预期工作。如果父视图在子视图上调用save方法,则不会应用对@State变量的更改 这是SwiftUI中的一个bug,还是我遗漏了什么?我已经创建了一个简单的playbook实现来演示这个问题 谢谢你的帮助Swift @State变量的值不会更改,swift,swiftui,Swift,Swiftui,我创建了一个视图,它提供了一个方便的保存按钮和保存方法。然后可以在父视图中使用这两种视图。 我们的想法是提供这些功能,以便可以自定义导航栏项,但保留原始实现 在视图中有一个绑定到@State变量的Textfield。如果从同一视图中调用save方法,则一切都会按预期工作。如果父视图在子视图上调用save方法,则不会应用对@State变量的更改 这是SwiftUI中的一个bug,还是我遗漏了什么?我已经创建了一个简单的playbook实现来演示这个问题 谢谢你的帮助 导入快捷界面 导入Playgr
导入快捷界面
导入PlaygroundSupport
结构ContentView:视图{
//创建子视图以使保存按钮在此视图中可用
var child=child()
变量体:某些视图{
导航视图{
导航链接(
目的地:child.navigationBarItems(
//将尾部按钮设置为子视图中的按钮。
//这是必需的,因为此视图可能位于模态内部
//工作表,我们需要添加取消按钮作为前导
//按钮:
//前导:self.cancel按钮
尾随:child.saveButton
)
) {
文本(“打开”)
}
}
}
}
结构子级:视图{
//存储textfield中的值
@国家私有var value=“默认”
//使此按钮在此视图和父视图中可用。
//这样可以确保此按钮的可见性始终相同。
var saveButton:某些视图{
按钮(操作:保存){
文本(“保存”)
}
}
变量体:某些视图{
VStack{
//允许更改字符串的简单文本字段。
文本字段(“值”,文本:$Value)
//只是为了便于操场更改值。
//通常是通过键盘输入来改变的。
按钮(操作:{
self.value=“新值”
}) {
文本(“更新”)
}
}
}
func save(){
//这始终显示状态变量的默认值。
//即使在使用了更新按钮之后,内部的值也发生了变化
//文本字段。
打印(“\(值)”)
}
}
PlaygroundPage.current.setLiveView(ContentView())
内部子项
:值
是可变的,因为它是用@State
包装的
在ContentView
内部:child
是不可变的,因为它没有用@State
包装
您的问题可以通过以下行解决:@State var child=child()
祝你好运。
子视图需要将其状态保持为绑定状态。这项工作:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@State var v = "default"
var body: some View {
let child = Child(value: $v)
return NavigationView {
NavigationLink(
destination: child.navigationBarItems(trailing: child.saveButton)
) {
Text("Open")
}
}
}
}
struct Child: View {
@Binding var value: String
var saveButton: some View {
Button(action: save) {
Text("Save")
}
}
var body: some View {
VStack {
TextField("Value", text: $value)
Button(action: {
self.value = "new value"
}) {
Text("Update")
}
}
}
func save() {
print("\(value)")
}
}
PlaygroundPage.current.setLiveView(ContentView())
TextField仅在按下return按钮时更新值绑定。要获取编辑期间发生的文本更改,请使用didSet在子对象上设置观察对象。这是我根据你的例子改变的操场
struct ContentView: View {
var child = Child()
var body: some View {
NavigationView {
NavigationLink(
destination: child.navigationBarItems(
trailing: child.saveButton
)
) {
Text("Open")
}
}
}
}
class TextChanges: ObservableObject {
var completion: (() -> ())?
@Published var text = "default" {
didSet {
print(text)
}
}
}
struct Child: View {
@ObservedObject var textChanges = TextChanges()
var saveButton: some View {
Button(action: save) {
Text("Save")
}
}
var body: some View {
VStack {
TextField("Value", text: $textChanges.text).multilineTextAlignment(.center)
Button(action: {
print(self.textChanges.text)
}) {
Text("Update")
}
}
}
func save() {
print("\(textChanges.text)")
}
}
PlaygroundPage.current.setLiveView(ContentView())
我认为更快捷的方法是:
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View {
return NavigationView {
// tell the child view where to render it's navigation item
// Instead of configuring navigation items.
NavigationLink(destination: Child(navigationSide: .left)) {
Text("Open")
}
}
}
}
struct Child: View {
enum NavigationSide { case left, right }
// If you really want to encapsulate all state in this view then @State
// is a good choice.
// If the parent view needs to read it, too, @Binding would be your friend here
@State private var value: String = "default"
// no need for @State as it's never changed from here.
var navigationSide = NavigationSide.right
// wrap in AnyView here to make ternary in ui code easier readable.
var saveButton: AnyView {
AnyView(Button(action: save) {
Text("Save")
})
}
var emptyAnyView: AnyView { AnyView(EmptyView()) }
var body: some View {
VStack {
TextField("Value", text: $value)
Button(action: {
self.value = "new value"
}) {
Text("Update")
}
}
.navigationBarItems(leading: navigationSide == .left ? saveButton : emptyAnyView,
trailing: navigationSide == .right ? saveButton : emptyAnyView)
}
func save() {
print("\(value)")
}
}
基于@nine stones(谢谢!)中的这一点,我实现了一种更快捷的方法来解决我的问题。它不允许像我计划的那样定制导航项,但这不是需要解决的问题。我想在导航链接和模式表中使用子视图。问题是如何执行自定义取消操作。这就是为什么我删除了按钮实现,并将其替换为cancelAction
闭包。现在,我可以在任何地方以任何方式显示子视图
有一件事我仍然不知道为什么SwiftUI没有将子上下文应用到saveButton
方法中的按钮
尽管如此,这是代码,也许它对将来的人有帮助
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
@Environment(\.presentationMode) var presentationMode
var body: some View {
NavigationView {
NavigationLink(
destination: Child(
// Instead of defining the buttons here, I send an optional
// cancel action to the child. This will make it possible
// to use the child view on navigation links, as well as in
// modal dialogs.
cancelAction: {
self.presentationMode.wrappedValue.dismiss()
}
)
) {
Text("Open")
}
}
}
}
struct Child: View {
// Store the value from the textfield
@State private var value = "default"
@Environment(\.presentationMode) var presentationMode
var cancelAction: (() -> Void)?
// Make this button available inside this view, and inside the parent view.
// This makes sure the visibility of this button is always the same.
var saveButton: some View {
Button(action: save) {
Text("Save")
}
}
var body: some View {
VStack {
// Simple textfield to allow a string to change.
TextField("Value", text: $value)
// Just for the playground to change the value easily.
// Usually it would be chnaged through the keyboard input.
Button(action: {
self.value = "new value"
}) {
Text("Update")
}
}
.navigationBarItems(
leading: self.cancelAction != nil ? Button(action: self.cancelAction!, label: {
Text("Cancel")
}) : nil,
trailing: self.saveButton
)
}
func save() {
// This always displays the default value of the state variable.
// Even after the Update button was used and the value did change inside
// the textfield.
print("\(value)")
}
}
PlaygroundPage.current.setLiveView(ContentView())
这不会改变结果,虽然我知道它是不可变的,但更改绑定的上下文应该是子视图。将var child
更改为@State
不幸没有任何区别。谢谢@jnblanchard。我可以证实这确实有效。你有关于原因的其他信息吗?如果我将文本(“\(名称)”)
放入我的代码中(在文本字段
之后),它会在我键入另一个字母后立即更新。此外,返回键似乎不会影响代码的任何更改。我使用了一个可观察对象将视图与文本数据连接起来。可观察对象上有一个属性观察者(didSet),负责文本值的更改。奇怪的是,在使用swiftui时,我通常会注意到在按下return键后值会更新。但我记得,除非我使用的是设备,而不是模拟器,否则它不会工作。很抱歉不清楚。我可以看到你是如何改变它的,以及为什么它会起作用。我的问题是,如果您有任何关于@State
实现不起作用的线索的话,我想问的更多。这主要是因为State将提供一个不可变的包装器。要真正了解这个值,您需要打开绑定。我还没有尝试过这个,但是通过在你的状态中使用wrappedValue属性,你可能会得到更改后的值。这确实如预期的那样有效。不幸的是,它向外部视图公开了很多样板文件,而外部视图只是用来修改导航栏项。您是否有任何其他信息说明此操作的原因?我认为您分配为navigationBarItem的saveButton已被捕获,包括它的操作对于您的流程来说太早了。太多的陈词滥调:嗯,我的回答是有点假设t