SwiftUI仿备忘录式底部Sheet组件是否为系统组件咨询
底部Sheet组件的实现方案
原生系统组件(iOS 15+)
SwiftUI从iOS 15开始提供了原生的底部可拖拽Sheet组件,基于UISheetPresentationController封装,用法和你熟悉的.sheet(isPresented:content:)完全适配,还能配置高度、拖拽指示器等系统原生交互效果,不用自己写手势动画就能达到符合预期的流畅表现。
核心用法示例:
struct ContentView: View { @State private var showSheet = false var body: some View { Button("打开底部Sheet") { showSheet.toggle() } .sheet(isPresented: $showSheet) { Text("底部Sheet内容") .padding() .presentationDetents([.medium, .large]) // 设置可停留高度:中等/全屏 .presentationDragIndicator(.visible) // 显示顶部拖拽条 } } }
presentationDetents:支持预设的.medium()(约屏幕一半高度)、.large()(全屏),也可以自定义固定高度(比如.height(300))presentationDragIndicator:控制顶部拖拽指示器的显示/隐藏- 系统原生支持拖拽调整高度、边缘手势关闭,动画效果和系统自带组件完全一致。
iOS 15以下兼容方案(自定义实现)
如果需要兼容更低版本,就得自行实现,但可以优化动画效果:
- 用
GeometryReader获取屏幕高度,确定Sheet的初始位置和拖拽范围 - 结合
DragGesture和withAnimation处理拖拽动画,手势结束时根据拖拽距离判断是展开还是收起 - 用
ZStack将Sheet放在底层内容上方,添加半透明背景遮罩
示例代码:
struct CustomBottomSheet: View { @Binding var isPresented: Bool let content: () -> View @State private var offsetY: CGFloat = 0 private let sheetHeight: CGFloat = 300 var body: some View { ZStack { // 背景遮罩 if isPresented { Color.black.opacity(0.3) .ignoresSafeArea() .onTapGesture { withAnimation(.easeInOut) { isPresented = false } } } // Sheet内容 VStack { // 拖拽指示器 Rectangle() .frame(width: 40, height: 6) .cornerRadius(3) .foregroundColor(.gray) .padding(.top, 8) content() } .frame(height: sheetHeight) .background(Color.white) .cornerRadius(16, corners: [.topLeft, .topRight]) .offset(y: isPresented ? offsetY + UIScreen.main.bounds.height - sheetHeight : UIScreen.main.bounds.height) .gesture( DragGesture() .onChanged { value in // 限制拖拽范围,不能超过初始位置 offsetY = max(value.translation.height, 0) } .onEnded { value in withAnimation(.easeInOut) { // 判断拖拽距离,超过三分之一就收起,否则回弹 if value.translation.height > sheetHeight / 3 { isPresented = false } offsetY = 0 } } ) } } } // 使用方式 struct ContentView: View { @State private var showCustomSheet = false var body: some View { Button("打开自定义底部Sheet") { showCustomSheet.toggle() } .overlay( CustomBottomSheet(isPresented: $showCustomSheet) { Text("自定义Sheet内容") .padding() } ) } }
- 用
offset(y:)控制Sheet的位置,结合动画让拖拽和收起动作更顺滑 - 添加背景遮罩,点击遮罩可快速关闭Sheet
- 拖拽指示器提升交互辨识度,手势结束的判断逻辑让动画更符合用户预期
内容的提问来源于stack exchange,提问作者마요Mayo
相关产品推荐
相关产品推荐

