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

SwiftUI Sheet的presentationDetents指定值在快速操作后失效

iOS 18+ 快速操作Sheet导致presentationDetents失效问题

问题现象

用Xcode 16.2构建项目,在iPhone 11(iOS 18.1.1/18.2)上快速多次通过向下滚动打开、关闭Sheet后,原本设置为presentationDetents([.medium])的Sheet会自动切换到.large尺寸显示,且后续无法恢复。相同代码在Xcode 15.4(对应iOS 17环境)下运行完全正常。

复现代码

import SwiftUI

struct ContentView: View {
    @State var isPresented = false
    
    var body: some View {
        VStack {
            Button("Show") {
                isPresented = true
            }
        }
        .padding()
        .sheet(isPresented: $isPresented, content: {
            Text("Sheet")
                .presentationDetents([.medium])
        })
    }
}

临时修复方案

这是iOS 18的SwiftUI底层bug,以下是几种可行的临时解决方式:

1. 禁用拖拽关闭功能

如果业务允许关闭Sheet的拖拽交互,可以彻底避免快速操作触发bug:

.sheet(isPresented: $isPresented, content: {
    Text("Sheet")
        .presentationDetents([.medium])
        .interactiveDismissDisabled() // 禁用拖拽关闭
})

2. 强制锁定Sheet尺寸

通过在Sheet出现后强制设置尺寸,修正系统的状态异常:

.sheet(isPresented: $isPresented, content: {
    Text("Sheet")
        .presentationDetents([.medium])
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
                guard let presentedVC = UIApplication.shared.windows.first?.rootViewController?.presentedViewController else { return }
                presentedVC.preferredContentSize = CGSize(
                    width: UIScreen.main.bounds.width,
                    height: UIScreen.main.bounds.height * 0.5
                )
            }
        }
})

3. 替换为自定义Sheet

如果需要保留拖拽关闭功能,完全自定义半屏弹窗是最稳妥的方案,彻底脱离系统Sheet的bug影响:

// 自定义半屏Sheet组件
struct CustomBottomSheet<Content: View>: View {
    @Binding var isShown: Bool
    let content: Content
    
    init(isShown: Binding<Bool>, @ViewBuilder content: () -> Content) {
        self._isShown = isShown
        self.content = content()
    }
    
    var body: some View {
        ZStack(alignment: .bottom) {
            if isShown {
                // 背景遮罩
                Color.black.opacity(0.3)
                    .ignoresSafeArea()
                    .onTapGesture { isShown = false }
                
                // 弹窗内容
                VStack(spacing: 0) {
                    content
                }
                .frame(height: UIScreen.main.bounds.height * 0.5)
                .background(.white)
                .cornerRadius(16, corners: [.topLeft, .topRight])
                .transition(.move(edge: .bottom))
                .gesture(
                    DragGesture()
                        .onEnded { value in
                            if value.translation.height > 50 {
                                isShown = false
                            }
                        }
                )
            }
        }
        .animation(.easeInOut(duration: 0.25), value: isShown)
    }
}

// 使用示例
struct ContentView: View {
    @State var isPresented = false
    
    var body: some View {
        VStack {
            Button("Show") {
                isPresented = true
            }
        }
        .padding()
        .overlay(CustomBottomSheet(isShown: $isPresented) {
            Text("Sheet")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(.gray.opacity(0.1))
        })
    }
}

额外建议

  • 优先尝试方案1或2,实现成本低,能快速缓解问题;
  • 若必须保留拖拽关闭,方案3的自定义组件是长期稳定的选择;
  • 建议通过苹果Feedback Assistant提交bug反馈,附上复现步骤和代码,推动官方修复。

内容的提问来源于stack exchange,提问作者M_Khater

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 01:05:16