SwiftUI .sheet() 关闭后body被异常计算且不呈现问题咨询
使用场景
若SwiftUI的ContentView()中通过@State属性presentSheet同时控制.sheet()弹窗的展示,以及PausableView()的状态,ContentView的body计算逻辑会随presentSheet的传值方式不同而发生变化:
struct ContentView: View { @State private var presentSheet = false var body: some View { return VStack{ Button("Show Sheet") { presentSheet.toggle() } PausableView(isPaused: presentSheet) // 1. 作为普通变量传值,弹窗关闭时会触发body和sheet重计算 // PausableView(isPaused: $presentSheet) // 2. 作为@Binding传值,弹窗关闭时属性变化不会触发body重计算 } .sheet(isPresented: $presentSheet) { DismissingView(isPresented: $presentSheet) } } }
- 将属性作为普通参数传入
PausableView(isPaused: presentSheet)时,弹窗关闭后,ContentView的body和已关闭的sheet的body都会被重新计算 - 将属性作为@Binding传入
PausableView(isPaused: $presentSheet)时,弹窗关闭后,ContentView的body和已关闭的sheet的body都不会被重新计算
疑问点
- 已关闭不再展示的sheet,其body仍被计算是否为SwiftUI的正常预期行为?
- 两种传值方式为何会导致完全不同的body计算逻辑?而子视图仅需要只读访问该属性,使用@Binding不符合设计规范,该如何处理该场景?
复现说明
该现象在iOS 14、iOS 15版本均可复现,Xcode 13构建的示例项目地址:https://github.com/clns/SwiftUI-sheet-redraw-on-dismiss,核心代码如下:
import SwiftUI struct DismissingView: View { @Binding var isPresented: Bool var body: some View { if #available(iOS 15.0, *) { print(Self._printChanges()) } else { print("DismissingView: body draw") } return VStack { Button("Dismiss") { isPresented.toggle() } Text("Dismissing Sheet").padding() }.background(Color.white) } } struct PausableView: View { var isPaused: Bool // @Binding var isPaused: Bool private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() @State private var counter = 0 var body: some View { Text("已过去秒数:\(counter)") .onReceive(timer) { _ in counter += isPaused ? 0 : 1 } } } struct ContentView: View { @State private var presentSheet = false var body: some View { if #available(iOS 15.0, *) { print(Self._printChanges()) } else { print("ContentView: body draw") } return VStack{ Button("展示弹窗") { presentSheet.toggle() } Text("如果@State属性`presentSheet`在视图的其他位置被使用(比如作为参数传入`PausableView(isPaused:presentSheet)`),弹窗关闭后ContentView的body和.sheet()的内容会被立刻重绘。但如果该属性以@Binding的形式传入`PausableView(isPaused:$presentSheet)`,则ContentView的body不会被重绘。").padding() PausableView(isPaused: presentSheet) // PausableView(isPaused: $presentSheet) } .sheet(isPresented: $presentSheet) { DismissingView(isPresented: $presentSheet) .background(BackgroundClearView()) // 用于查看弹窗下层的内容变化 } } }
该问题同时发布在Apple Developer Forums:https://developer.apple.com/forums/thread/691783
内容的提问来源于stack exchange,提问作者clns
相关产品推荐
相关产品推荐

