首次呈现时,SwiftUI 显示带有绑定变量的工作表不起作用

问题描述 投票:0回答:5

我正在尝试在工作表中呈现一个带有 @Binding String 变量的视图,该变量仅在 TextField 中显示/绑定此变量。

在我的主 ContentView 中,我有一个字符串数组,我用 ForEach 循环显示该数组的索引,显示一个按钮,每个按钮都带有循环元素的文本。

按钮操作很简单:将 @State“索引”变量设置为按下的按钮的元素索引并显示工作表。

这是我的内容视图:

struct ContentView: View {
    
    @State var array = ["first", "second", "third"]
    @State var showIndex = 0
    @State var showSheet = false
    
    var body: some View {
        VStack {
            ForEach (0 ..< array.count, id:\.self) { i in
                Button("\(array[i])") {
                    showIndex = i
                    showSheet = true
                }
            }
            // Text("\(showIndex)") // if I uncomment this line, it works!
        }
        .sheet(isPresented: $showSheet, content: {
            SheetView(text: $array[showIndex])
        })
        .padding()
    }
}

这是 SheetView:

struct SheetView: View {
    @Binding var text: String
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack {
            TextField("text:", text: $text)
            Button("dismiss") {
                presentationMode.wrappedValue.dismiss()
            }
        }.padding()
    }
}

问题是,当我第一次打开应用程序并按“第二个”按钮时,工作表将打开并在文本字段中显示“第一个”。然后我可以关闭工作表并再次按“第二个”按钮,得到相同的结果。

如果我按下“第三个”或“第一个”按钮,从那时起一切都会正常。按任何按钮都会产生正确的行为。

Preview

有趣的是,如果我取消注释显示 showIndex 变量的文本行,它从第一次开始就有效。

这是一个错误,还是我在这里做错了什么?

macos swiftui binding state
5个回答
5
投票

您应该使用自定义 Binding、自定义 Struct 来解决问题,这是一个复杂的问题。请参阅示例:

struct ContentView: View {
    
    @State private var array: [String] = ["first", "second", "third"]
    @State private var customStruct: CustomStruct?
    
    
    var body: some View {
        VStack {
            
            ForEach (array.indices, id:\.self) { index in
                
                Button(action: { customStruct = CustomStruct(int: index) }, label: {
                    Text(array[index]).frame(width: 100)
                    
                })
                
            }
            
        }
        .frame(width: 300, height: 300, alignment: .center)
        .background(Color.gray.opacity(0.5))
        .sheet(item: $customStruct, content: { item in SheetView(text: Binding.init(get: { () -> String in return array[item.int] },
                                                                                    set: { (newValue) in array[item.int] = newValue }) ) })
    }
}



struct CustomStruct: Identifiable {
    let id: UUID = UUID()
    var int: Int
}



struct SheetView: View {
    @Binding var text: String
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack {
            TextField("text:", text: $text)
            Button("dismiss") {
                presentationMode.wrappedValue.dismiss()
            }
        }.padding()
    }
}


1
投票

我以前也遇到过这种情况。我相信这是一个错误,因为在 UI 中使用它之前,它似乎没有在 ForEach 中设置。我基本上以与您相同的方式修复了它,但有一点微妙。在每个

Button
中使用它作为
Label
的一部分,但像这样隐藏它:

Button(action: {
    showIndex = i
    showSheet = true
}, label: {
    HStack {
        Text("\(array[i])")
        Text(showIndex.description)
            .hidden()
    }
})

这不会改变您的 UI,但您使用它时它会得到正确更新。我似乎找不到我的应用程序中出现问题的位置,并且我更改了用户界面以摆脱此问题,但我不记得我是如何做到的。如果我能找到它,我会更新它。这有点拼凑,但它确实有效。


0
投票

将绑定传递到索引可以解决这样的问题

 struct ContentView: View {
   
    @State var array = ["First", "Second", "Third"]
    @State var showIndex: Int = 0
    @State var showSheet = false
    
    var body: some View {
        VStack {
            ForEach (0 ..< array.count, id:\.self) { i in
                Button(action:{
                    showIndex = i
                    showSheet.toggle()
                })
                {
                    Text("\(array[i])")
                }.sheet(isPresented: $showSheet){
                    SheetView(text: $array, index: $showIndex)
                }
            }
        }
        .padding()
    }
}

struct SheetView: View {
    @Binding var text: [String]
    @Binding var index: Int
    @Environment(\.presentationMode) var presentationMode
    
    var body: some View {
        VStack {
            TextField("text:", text: $text[index])
            Button("dismiss") {
                presentationMode.wrappedValue.dismiss()
            }
        }.padding()
    }
}

在 SwiftUI2 中,当调用 isPresented 时,如果不传递绑定,将会遇到一些奇怪的问题。 如果您想将其与 isPresented 一起保留并使其工作,这是一个简单的调整,但我建议您使用带有服装结构的项目,如 swiftPunk 的答案


0
投票

我就是这么做的。如果不使用 @State 变量,您将丢失表单编辑。

此代码未经测试

struct SheetView: View {
    @Binding var text: String
    @State var draft: String
    @Environment(\.presentationMode) var presentationMode
    
    init(text: Binding<String>) {
        self._text = text
        self._draft = State(initialValue: text.wrappedValue)
    }

    var body: some View {
        VStack {
            TextField("text:", text: $draft)
            Button("dismiss") {
                text = draft
                presentationMode.wrappedValue.dismiss()
            }
        }.padding()
    }
}

0
投票

我能够通过使用不同的工作表呈现修饰符(使用实体的可为空性来确定是否应呈现工作表)很好地解决此问题

MyView()
    .sheet(item: $item) { item in
        MySheet(item: item)
    }

而不是

MyView()
    .sheet(isPresented: $isPresented) {
        if let item {
            MySheet(item: item)
        }
    }

在这种简化的场景下,这种方式无疑是更好的。但在实践中,视图可能需要进行一些重构才能以这种方式工作(例如,将内容分组到结构中)。

我同样使用了

ForEach
以及通过子视图中的
@State
更新
@Binding
的内容。

© www.soinside.com 2019 - 2024. All rights reserved.