iOS 18.3.1下SwiftUI Sheet通过代码关闭时的交互延迟与视图不一致问题
我完全懂你遇到的这个糟心问题——在iOS 18.3.1上,用按钮触发代码关闭Sheet时(不管是设置sheetIsPresented = false还是调用dismiss()),不仅onDismiss的回调会延迟触发,就连你明明已经设置为启用的背景交互也会被莫名锁死,直到延迟的onDismiss执行完毕才恢复。但滑动关闭Sheet时一切正常,这种不一致的体验确实很影响用户感受。
问题根源分析
这个现象大概率是iOS 18.3.1的系统级Bug。SwiftUI在处理代码触发的Sheet关闭流程时,似乎没有正确同步presentationBackgroundInteraction的状态,同时onDismiss的回调被系统调度延迟了,导致Sheet视觉上已经消失,但背景UI仍处于不可交互的锁定状态,必须等onDismiss触发后才会解锁。
可行的解决方案
既然是系统层面的Bug,我们可以通过手动同步状态的方式来规避,下面给你两个实用的解决思路:
方案1:主动触发关闭逻辑,绕过系统延迟的onDismiss
把onDismiss里的业务逻辑抽成独立方法,在手动关闭Sheet的同时直接调用这个方法,这样就能保证状态更新和Sheet关闭同步,不用傻等系统的延迟回调。修改后的完整代码如下:
import SwiftUI struct ContentView: View { @State var sheetIsPresented: Bool = false @State var count: Int = 0 @State var color: Color = .white // 抽离onDismiss的核心业务逻辑 private func handleSheetDismiss() { color = .random } var body: some View { ZStack { color .ignoresSafeArea() VStack { HStack { Text(String(count)) Button("Count +1") { count += 1 } } Button("Show sheet") { sheetIsPresented = true } } .padding() .background(Color(.white).shadow(radius: 2)) } .sheet(isPresented: $sheetIsPresented, onDismiss: handleSheetDismiss) { VStack { Button("isPresented = false") { sheetIsPresented = false // 主动触发关闭逻辑,不用等系统回调 handleSheetDismiss() } SheetView(onDismiss: handleSheetDismiss) .presentationDetents([.fraction(0.49)]) .presentationBackgroundInteraction(.enabled) .presentationDragIndicator(.visible) Spacer() } .padding(.top, 25) } } } struct SheetView: View { @Environment(\.dismiss) var dismiss let onDismiss: () -> Void // 接收外部传入的关闭回调 var body: some View { Button("Dismiss via dismiss()") { dismiss() // 主动触发关闭逻辑 onDismiss() } } } extension Color { static var random: Color { return Color(hue: Double.random(in: 0...1), saturation: Double.random(in: 0...1), brightness: Double.random(in: 0...1)) } } #Preview { ContentView() }
这样修改后,无论你用哪种代码方式关闭Sheet,背景色都会立即更新,同时背景的计数器按钮也能马上恢复交互,完全不会再出现延迟锁死的情况。
方案2:用withAnimation包装关闭操作
另一种思路是给Sheet的关闭操作加上显式动画,强制系统同步处理Sheet消失动画和背景交互的状态切换:
比如修改isPresented = false的按钮:
Button("isPresented = false") { withAnimation { sheetIsPresented = false } }
修改dismiss()的按钮:
Button("Dismiss via dismiss()") { withAnimation { dismiss() } }
这种方式能一定程度上缓解延迟问题,但效果可能不如方案1直接彻底,适合不想大幅改动原有代码结构的场景。
额外建议
这个问题属于iOS 18的版本特定Bug,建议你通过Apple的反馈助手提交报告,附上你的测试代码和问题复现步骤,帮助Apple在后续版本中修复这个问题。
内容来源于stack exchange

