[英]Bug with NavigationLink and @Binding properties causing unintended Interactions in the app
我在 SwiftUI 中遇到了一个错误,它可能会导致在用户不知情的情况下与应用程序进行意外交互。
该问题似乎与在与 NavigationStack 和 NavigationLink 结合使用时在View 结构上使用@Binding 属性有关。 如果您将 NavigationView 与 NavigationLink 一起使用来显示接受 $Binding 参数的 DetailView,并且该参数在 DetailView 中的某种条件下使用,则会导致意外行为。
为了清楚地显示问题,我使用了 DetailView,其中根据 @Binding 属性显示“蓝色”或“红色”视图。 这些视图中的每一个都有一个.onTapGesture() 修饰符,可以在点击时打印一些文本。 问题在于,如果显示红色视图,它会检测并触发蓝色视图上的操作,这可能会导致许多应用程序在用户不知情的情况下发生意外更改。
您可以轻松地将此代码复制并粘贴到您自己的文件中以复制错误。 要查看意外行为,请运行以下代码并在模拟器上执行以下步骤:
我在以下设备上测试了此行为:XCode 14.1、iPhone 13 Pro 16.1 iOS 模拟器,以及带有 iOS 16 的真实 iPhone。结果始终相同。
import SwiftUI
struct ContentView: View {
var body: some View {
NavView()
}
}
struct NavView: View {
@State private var colourShowed: Int = 1
var body: some View {
// If the DetailView() was shown directly, (without the NavigationLink and NavigationStack) there would be no such a bug.
// DetailView(colourShowed: $colourShowed)
// The bug is obvious when using the NavigationStack() with the NavigationLink()
NavigationStack {
Form {
NavigationLink(destination: { DetailView(colourShowed: $colourShowed) },
label: { Text("Detail View") })
}
}
}
}
struct DetailView: View {
// It seems like the problem is related to this @Binding property when used in conjunction
// with the NavigationLink in "NavView" View above.
@Binding var colourShowed: Int
var body: some View {
ScrollView {
VStack(spacing: 20){
HStack {
Button("BLUE BUTTON", action: {colourShowed = 1})
Spacer()
Button("RED BUTTON", action: {colourShowed = 2})
}
if colourShowed == 1 {
Color.blue
.frame(height: 500)
// the onTapeGesture() is stillActive here even when the "colourShowed" property is set to '2' so this
// view should therefore be deinitialized.
.onTapGesture {
print("BLUE tapped")
}
// The onAppear() doesn't execute when switching from the Red view to the Blue view.
// It seems like the "Blue" View does not deinitialize itself after being previously shown.
.onAppear(perform: {print("Blue appeared")})
}
else {
Color.red
.frame(height: 100)
.onTapGesture {
print("RED tapped")
}
.onAppear(perform: {print("Red appeared")})
}
}
}
}
}
有什么解决方案可以防止这种情况发生吗?
这是 Swift 和值语义的新手遇到的常见问题,您可以使用称为“捕获列表”的东西来解决它,如下所示:
NavigationLink(destination: { [colourShowed] in
发生这种情况是因为 DetailView 在更改时没有使用colourShowed
的新值重新初始化。 body 中没有任何内容使用它,因此 SwiftUI 的依赖项跟踪并不认为 body 必须重新计算。 但是由于您依赖于使用新值初始化 DetailView,因此您必须将其添加到捕获列表以强制重新计算 body 并初始化一个新的 DetailView。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.