@State和@Binding如何并行使用?
How to use @State and @Binding parallel?
我的设置:
我有一个 ContentView
代表最终 selection
(selection.count
) 的长度。因此,我需要在 ContentView
上使用 @State
propertyWrapper
的 selection
变量,因为我希望 View
在值更改时立即更新。该选择应该在我的 SelectionView
上进行,因此我在 ContentView
和 SelectionView
上的 selection
变量之间创建了一个 Binding
。
我的问题: 我的 SelectionView
上的 UI 也应该在 selection
变量更改时更新,但由于它使用 @Binding
而不是 @State
视图不会更新。所以我需要一些可以同时使用 @State
和 @Binding
或 @Binding
的东西,它也可以使 UI 重新加载。
struct ContentView: View {
@State var selection: [Int] = []
var body: some View {
NavigationView {
Form {
NavigationLink(destination: SelectionView(selection: $selection)) {
Text("Selection: \(selection.count)")
}
}
}
}
}
struct SelectionView: View {
@Binding var selection: [Int]
var body: some View {
NavigationView {
Form {
ForEach((0...9).identified(by: \.self)) { i in
Button(action: {
if self.selection.contains(i) {
self.selection = self.selection.filter { !([=10=] == i) }
} else {
self.selection.append(i)
}
}) {
if self.selection.contains(i) {
Text("Unselect \(i)")
} else {
Text("Select \(i)")
}
}
}
}
}
}
}
注意: 如果我在 SelectionView
上使用 @State
而不是 @Binding
它可以正常工作(这显然需要我不创建我想要的绑定)。
你的绑定没有问题。这是正确的做法,并且会按照您希望的方式工作。绑定是您在 SwiftUI 中传递可变状态的方式,您正在这样做,并且它有效。对绑定的更改 确实 使视图重新加载。
为了让自己相信这一点,只需去掉示例中的所有额外内容,并专注于问题的核心,即绑定:
struct ContentView: View {
@State var selection: [Int] = []
var body: some View {
NavigationView {
Form {
NavigationLink(destination: SelectionView(selection: $selection)) {
Text("Selection: \(selection.count)")
}
}
}
}
}
struct SelectionView: View {
@Binding var selection: [Int]
var body: some View {
NavigationView {
VStack {
Button.init("Append") {
self.selection.append(1)
}
Text("Selection: \(selection.count)")
}
}
}
}
运行例子,点击link,重复点击按钮。 selection.count
的显示在两个视图中都发生了变化。这就是你想要的,这就是发生的事情。
这是原始代码的一个变体,它更明确地显示 selection
(而不是 selection.count
),您可以看到正确的事情正在发生:
struct ContentView: View {
@State var selection: [Int] = []
var body: some View {
NavigationView {
Form {
NavigationLink(destination: SelectionView(selection: $selection)) {
Text("Selection: \(String(describing:selection))")
}
}
}
}
}
struct SelectionView: View {
@Binding var selection: [Int]
var body: some View {
NavigationView {
List {
ForEach(0...9, id:\.self) { i in
Button(action: {
if let ix = self.selection.firstIndex(of:i) {
self.selection.remove(at: ix)
} else {
self.selection.append(i)
}
}) {
if self.selection.contains(i) {
Text("Unselect \(i)")
} else {
Text("Select \(i)")
}
}
}
}
}
}
}
您的问题的解决方法是:升级到最新的Xcode。
我在操场上用 Xcode 11 beta 4 测试了你的代码,它工作正常。
我在 Xcode 11 beta 3 下的操场上测试了你的代码,但它以你描述的方式失败了(我认为)。
截至此答案,Xcode 11 的当前版本是 beta 5,在该版本下您的代码无法编译,因为 identified(by:)
修饰符已被删除。当我将您的代码更改为在 beta 5 下工作时,通过将 ForEach((0...9).identified(by: \.self))
替换为 ForEach(0...9, id: \.self)
,它工作正常。
因此我推断您仍然是 运行 beta 2 或 beta 3。(Form
在 beta 1 中不可用,所以我知道您没有使用该版本。)
请理解,目前,SwiftUI 仍然存在很多问题,并且仍在进行不兼容的 API 更改。错误是不幸的,但 API 进化是 好 。我们现在经历几个月的变化总比以后经历几年不太理想的变化要好API。
这意味着您需要尝试使用最新的 Xcode 11 测试版,除非它引入了错误(例如 beta 5 中的 Path
错误,如果您的应用使用 Path
) 阻碍你取得任何进展。
感谢@robmayoff 我能够解决问题。 Xcode 11 Beta 3 有问题,安装最新的测试版解决了问题
我的设置:
我有一个 ContentView
代表最终 selection
(selection.count
) 的长度。因此,我需要在 ContentView
上使用 @State
propertyWrapper
的 selection
变量,因为我希望 View
在值更改时立即更新。该选择应该在我的 SelectionView
上进行,因此我在 ContentView
和 SelectionView
上的 selection
变量之间创建了一个 Binding
。
我的问题: 我的 SelectionView
上的 UI 也应该在 selection
变量更改时更新,但由于它使用 @Binding
而不是 @State
视图不会更新。所以我需要一些可以同时使用 @State
和 @Binding
或 @Binding
的东西,它也可以使 UI 重新加载。
struct ContentView: View {
@State var selection: [Int] = []
var body: some View {
NavigationView {
Form {
NavigationLink(destination: SelectionView(selection: $selection)) {
Text("Selection: \(selection.count)")
}
}
}
}
}
struct SelectionView: View {
@Binding var selection: [Int]
var body: some View {
NavigationView {
Form {
ForEach((0...9).identified(by: \.self)) { i in
Button(action: {
if self.selection.contains(i) {
self.selection = self.selection.filter { !([=10=] == i) }
} else {
self.selection.append(i)
}
}) {
if self.selection.contains(i) {
Text("Unselect \(i)")
} else {
Text("Select \(i)")
}
}
}
}
}
}
}
注意: 如果我在 SelectionView
上使用 @State
而不是 @Binding
它可以正常工作(这显然需要我不创建我想要的绑定)。
你的绑定没有问题。这是正确的做法,并且会按照您希望的方式工作。绑定是您在 SwiftUI 中传递可变状态的方式,您正在这样做,并且它有效。对绑定的更改 确实 使视图重新加载。
为了让自己相信这一点,只需去掉示例中的所有额外内容,并专注于问题的核心,即绑定:
struct ContentView: View {
@State var selection: [Int] = []
var body: some View {
NavigationView {
Form {
NavigationLink(destination: SelectionView(selection: $selection)) {
Text("Selection: \(selection.count)")
}
}
}
}
}
struct SelectionView: View {
@Binding var selection: [Int]
var body: some View {
NavigationView {
VStack {
Button.init("Append") {
self.selection.append(1)
}
Text("Selection: \(selection.count)")
}
}
}
}
运行例子,点击link,重复点击按钮。 selection.count
的显示在两个视图中都发生了变化。这就是你想要的,这就是发生的事情。
这是原始代码的一个变体,它更明确地显示 selection
(而不是 selection.count
),您可以看到正确的事情正在发生:
struct ContentView: View {
@State var selection: [Int] = []
var body: some View {
NavigationView {
Form {
NavigationLink(destination: SelectionView(selection: $selection)) {
Text("Selection: \(String(describing:selection))")
}
}
}
}
}
struct SelectionView: View {
@Binding var selection: [Int]
var body: some View {
NavigationView {
List {
ForEach(0...9, id:\.self) { i in
Button(action: {
if let ix = self.selection.firstIndex(of:i) {
self.selection.remove(at: ix)
} else {
self.selection.append(i)
}
}) {
if self.selection.contains(i) {
Text("Unselect \(i)")
} else {
Text("Select \(i)")
}
}
}
}
}
}
}
您的问题的解决方法是:升级到最新的Xcode。
我在操场上用 Xcode 11 beta 4 测试了你的代码,它工作正常。
我在 Xcode 11 beta 3 下的操场上测试了你的代码,但它以你描述的方式失败了(我认为)。
截至此答案,Xcode 11 的当前版本是 beta 5,在该版本下您的代码无法编译,因为 identified(by:)
修饰符已被删除。当我将您的代码更改为在 beta 5 下工作时,通过将 ForEach((0...9).identified(by: \.self))
替换为 ForEach(0...9, id: \.self)
,它工作正常。
因此我推断您仍然是 运行 beta 2 或 beta 3。(Form
在 beta 1 中不可用,所以我知道您没有使用该版本。)
请理解,目前,SwiftUI 仍然存在很多问题,并且仍在进行不兼容的 API 更改。错误是不幸的,但 API 进化是 好 。我们现在经历几个月的变化总比以后经历几年不太理想的变化要好API。
这意味着您需要尝试使用最新的 Xcode 11 测试版,除非它引入了错误(例如 beta 5 中的 Path
错误,如果您的应用使用 Path
) 阻碍你取得任何进展。
感谢@robmayoff 我能够解决问题。 Xcode 11 Beta 3 有问题,安装最新的测试版解决了问题