SwiftUI:显示工作表时绑定的 @State 变量未更新

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

我在 SwiftUI 中遇到一个问题,其中绑定的 @State 变量 (displayString) 在呈现工作表时似乎没有及时更新。提供的代码由父视图(SimpleParentView)和子视图(SimpleChildView)组成。当 showSheet 设置为 true 时,父视图呈现一个工作表,并且该工作表显示 displayString 值。

第一次按下 SimpleChildView 中的“按钮 1”或“按钮 2”时,就会出现问题。当按下时,它会更新 displayString,然后通过将 showSheet 设置为 true 来显示工作表。但是,当第一次按下按钮时显示工作表时,它仍然显示“初始字符串”,表明显示字符串尚未及时更新以显示工作表。

有趣的是,如果用户关闭工作表,然后再次按下相反的按钮或相同的按钮,工作表会正确显示更新后的显示字符串,并且任何后续按钮按下也会按预期工作。

我尝试使用 DispatchQueue.main.asyncAfter 延迟将 showSheet 设置为 true,希望这能为 displayString 提供足够的时间来更新,但这种方法似乎没有任何区别。

我也厌倦了通过首先分配一个废弃值来“启动”分配,希望这能解决最初神秘的错误分配,但这也不起作用。

为什么会出现这种情况?

import Foundation

import SwiftUI

struct SimpleParentView: View {
    @State private var showSheet = false
    @State private var displayString = "Initial String"
    
    var body: some View {
        VStack {
            SimpleChildView(showSheet: $showSheet, displayString: $displayString)
        }
        .sheet(isPresented: $showSheet) {
            VStack {
                Text(displayString)
                Button("Close") {
                    showSheet = false
                }
                .padding()
            }
        }
    }
}

struct SimpleChildView: View {
    @Binding var showSheet: Bool
    @Binding var displayString: String
    
    var body: some View {
        Button(action: {
            // This "priming the assignment" hack makes no difference
            // displayString = ""
            displayString = "Updated String 1"
            showSheet = true
            // This delayed assignment hack makes no difference
            /*
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                showSheet = true
            }*/
        }) {
            Text("Button 1 (show sheet in parent)")
        }
        Button(action: {
            displayString = "Updated String 2"
            showSheet = true
        }) {
            Text("Button 2 (show sheet in parent)")
        }
    }
}

struct SimpleParentView_Previews: PreviewProvider {
    static var previews: some View {
        SimpleParentView()
    }
}
swift swiftui binding state swiftui-state
1个回答
0
投票

这似乎不是故意的,可能是 SwiftUI 中的一个错误。

看来 SwiftUI 制作了工作表的初始版本,并表明当

showSheet
变为 true 时,没有注意到工作表的内容使用
displayString
,因此应该重新绘制。
ContentView.body
仅评估一次,直到您按下不同的按钮。

ContentView
body
(不包括纸张)不使用
displayString
- 仅使用它的装订。这可能导致 SwiftUI 认为它不需要更新。但这只是猜测。

无论如何,如果你让

ContentView
displayString
有最轻微的依赖,这种行为就不会发生。这可能是一个隐藏的
Text
:

Text(displayString).hidden()

甚至只是一个

onChange
修饰符:

VStack {
    ...
}
.onChange(of: displayString) { _ in }

这是我最喜欢的 - 即使只是将

displayString
let _ =
一起丢弃也有效:

VStack {
    let _ = displayString
    SimpleChildView(showSheet: $showSheet, displayString: $displayString)
        .sheet(...) { ... }
}
© www.soinside.com 2019 - 2024. All rights reserved.