SwiftUI @State和.sheet() ios13与ios14的区别

12

你好,我在这里遇到了一个问题,而且当我在iOS13或iOS14上运行时,我的.sheet()视图的行为并不一致。

我得到了这样的视图:

@State private var label: String = "" 
@State private var sheetDisplayed = false
///Some code
var body: some View {
   VStack {
      Button(action: {
         self.label = "A label"
         self.isDisplayed = true
      }) {
           Text("test")
       }
   }.sheet(isPresented: $sheetDisplayed, onDismiss: {
        self.label = ""
    }) {
        Text(self.label)
       }
 }

在iOS 13上,此操作按预期工作:点击按钮 -> 设置标签 -> 调用Sheet -> 在文本视图中显示“ A label”。

在iOS14上,在sheet闭包中的self.label返回一个空字符串,因此它不会显示任何内容。

我错过了什么吗?这是iOS 14的错误还是我在iOS 13上做错了,并且已经得到了纠正。

附注:我的闭包中还有其他变量,我将其简化了。


看起来表格是在设置标签之前创建的。您不应该依赖于SwiftUI创建视图的顺序,但在这种情况下,我认为这是一个值得提交给Apple的错误。 - pawello2222
在iOS 15中发生了相同的事情。 - Rob N
感谢您的评论。如果您遇到问题,请查看已接受的答案,它仍然适用于iOS 15。 - FitzChill
3个回答

11

你的代码期望视图更新/创建的顺序,但通常情况下它是未定义的(并且可能在iOS 14中发生了更改)。

有一种明确的方法可以在sheet内传递信息 - 使用不同的sheet创建器,即.sheet(item:...

这里是一个可靠的工作示例。经过Xcode 12 / iOS 14测试。

struct ContentView: View {
    @State private var item: Item?

    struct Item: Identifiable {
        let id = UUID()
        var label: String = ""
    }

    var body: some View {
        VStack {
            Button(action: {
                self.item = Item(label: "A label")
            }) {
                Text("test")
            }
        }.sheet(item: $item, onDismiss: {
            self.item = nil
        }) {
            Text($0.label)
        }
    }
}

我能够使用你的解决方案使其工作,谢谢。但仍然很奇怪,当添加didSet属性观察器并打印值(或在调试中)时,我的值在进入该表格内容闭包之前就已经发生了变化,但在表格中仍然为nil。 - FitzChill

4

这是iOS 14中的一些非常奇怪的行为,似乎没有被记录在文档中。

使用这里的其他答案此线程上的评论,我使用@Binding解决了该问题,因为它看起来是最清洁和最符合SwiftUI风格的解决方案。

我不知道为什么会有这种变化,它似乎比之前更不直观,所以我认为这是一个bug!

以下是一个示例:

struct MainView: View {
    @State private var message = ""
    @State private var showSheet = false

    var body: some View {
        Button(action: {
            self.message = "This will display the correct message"
            self.showSheet = true
        }, label: {
            Text("Test Button")
        })
        .sheet(isPresented: self.$showSheet) {
            SheetView(message: self.$message)
        }
    }
}

struct SheetView: View {
    @Binding var message: Int

    var body: some View {
        Text(self.message)
    }
}

1
谢谢更新,我已经有几天没有看过那个帖子了,但我记得尝试过这个方法,但没有成功(至少在我的情况下,我在这里过度简化了我的问题)。但是我在我的表格闭包中有一个可选项为空,导致应用程序崩溃。 - FitzChill
1
你能解释一下为什么需要传递 Binding 吗?为什么仅传递包装值不起作用?我在我的代码中测试了这个解决方案,添加魔法 $ 并稍后解包该值就可以正常工作... - rraszewski
1
我猜测因为该表格能够修改绑定的值,所以它需要作为一个绑定。如果使用普通的值/引用对象,它将在父视图中丢失。此外,这是由sheet()构造函数所要求的(无论是isPresented: Binding<Bool>还是item:Binding<Item?>)。请参考此处的链接:https://developer.apple.com/documentation/swiftui/view/sheet(ispresented:ondismiss:content:)。 - FitzChill

0

SwiftUI 2.0 的行为有所改变,这也影响到 MacOS 11,只需将绑定添加到视图中即可修复它,即使从未使用该绑定,这让我认为这是一个实现错误。 此外,在视图的正文中使用 details 状态变量也可以修复它。

struct MyViewController : View {

    @State var details: String?
    @State var showDetails = false

    //    @Binding var havingAbindingFixesIt: String?

    var body: some View {
        VStack {
            //            Text(details ?? "")
            Text("Tap here for details")
                .onTapGesture {
                    self.details = "These are the details"
                    self.showDetails.toggle()
                }
                .sheet(isPresented: $showDetails) { Text(details ?? "") }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接