You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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都不会被重新计算

疑问点

  1. 已关闭不再展示的sheet,其body仍被计算是否为SwiftUI的正常预期行为?
  2. 两种传值方式为何会导致完全不同的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:06:01