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

SwiftUI Sheet键盘动画Bug:设置presentationBackground与medium detent时异常

修复思路与解决方案

针对Xcode 16中SwiftUI Sheet的这个键盘间隙Bug,这里提供几个可行的修复方向:

方案1:将背景嵌入Sheet内部而非使用presentationBackground

系统的presentationBackground在结合.medium detent和键盘交互时存在布局计算错误,我们可以把背景直接放在Sheet的根视图里,绕开系统的背景布局逻辑:

.sheet(isPresented: $isPresented) {
  ZStack {
    // 自定义背景,替代presentationBackground
    Color.red.opacity(0.75)
      .ignoresSafeArea()
    
    ScrollView {
      TextField("Enter text here", text: .constant(""))
        .padding()
    }
    .scrollDismissesKeyboard(.interactively)
  }
  .presentationDetents([.medium, .large])
}

这个方法让背景和Sheet内容共享同一个布局上下文,避免系统单独计算背景位置时出错。

方案2:限制presentationBackground的布局范围

通过给presentationBackground添加明确的frame约束,强制它跟随Sheet的实际可视区域:

.sheet(isPresented: $isPresented) {
  ScrollView {
    TextField("Enter text here", text: .constant(""))
      .padding()
  }
  .scrollDismissesKeyboard(.interactively)
  .presentationDetents([.medium, .large])
  .presentationBackground {
    Color.red.opacity(0.75)
      .frame(maxWidth: .infinity, maxHeight: .infinity)
      .ignoresSafeArea()
  }
}

这里给背景视图设置无限宽高并忽略安全区,强制它填满Sheet的整个可用空间,减少系统布局计算的偏差。

方案3:临时规避:监听键盘状态调整Detent

如果上面的方法都不生效,可以用一个临时的 workaround:监听键盘的显示/隐藏状态,在键盘收起时强制切换一次detent,触发Sheet重新布局:

首先添加键盘监听工具类:

import Combine

class KeyboardObserver: ObservableObject {
  @Published var isKeyboardVisible = false
  
  private var cancellables = Set<AnyCancellable>()
  
  init() {
    NotificationCenter.default.publisher(for: UIResponder.keyboardWillShowNotification)
      .sink { [weak self] _ in
        self?.isKeyboardVisible = true
      }
      .store(in: &cancellables)
    
    NotificationCenter.default.publisher(for: UIResponder.keyboardWillHideNotification)
      .sink { [weak self] _ in
        self?.isKeyboardVisible = false
      }
      .store(in: &cancellables)
  }
}

然后在视图中使用:

struct ContentView: View {
  @State var isPresented = false
  @State private var selectedDetent: PresentationDetent = .medium
  @StateObject private var keyboardObserver = KeyboardObserver()

  var body: some View {
    VStack {
      Image(systemName: "globe")
        .imageScale(.large)
        .foregroundStyle(.tint)

      Button("Present sheet") {
        self.isPresented.toggle()
      }
    }
    .sheet(isPresented: $isPresented) {
      ScrollView {
        TextField("Enter text here", text: .constant(""))
          .padding()
      }
      .scrollDismissesKeyboard(.interactively)
      .presentationDetents([.medium, .large], selection: $selectedDetent)
      .presentationBackground(Color.red.opacity(0.75))
      .onChange(of: keyboardObserver.isKeyboardVisible) { oldValue, newValue in
        // 键盘收起时,临时切换detent再切回,触发重布局
        if !newValue && selectedDetent == .medium {
          selectedDetent = .large
          DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            selectedDetent = .medium
          }
        }
      }
    }
    .padding()
  }
}

这个方法通过强制触发Sheet的布局更新消除间隙,但属于临时 workaround,建议优先用前两个方案。

额外建议

这个问题是Xcode 16的SwiftUI框架Bug,建议通过Apple的Feedback Assistant提交反馈,附上复现代码和屏幕录制,帮助官方修复问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:45:10