SwiftUI开发:如何在Sheet上方固定位置放置跟随移动的按钮?
实现苹果地图式跟随Sheet移动的顶部控件
要让控件跟随底部Sheet同步移动,核心是将控件与Sheet的位置变化绑定,以下是两种可行实现方案:
方案一:基于预设Detent状态同步位置
适合使用固定高度档位(如medium、large、自定义高度)的Sheet场景:
- 定义绑定变量跟踪当前Sheet的档位:
@State private var selectedDetent: PresentationDetent = .height(80)
- 在视图中添加跟随控件,并根据档位计算偏移量:
struct ContentView: View { @State private var selectedDetent: PresentationDetent = .height(80) var body: some View { ZStack { HomeView() // 跟随Sheet移动的控件组(示例:Look Around按钮+AQI显示) HStack { Button(action: {}) { Image(systemName: "binoculars") .padding() .background(.white) .cornerRadius(12) } Spacer() Text("AQI 42 良") .padding(.horizontal, 16) .padding(.vertical, 8) .background(.white) .cornerRadius(12) } .padding(.horizontal, 16) .offset(y: calculateOffset(for: selectedDetent)) .sheet(isPresented: .constant(true)) { ActivityView() .presentationDetents([.medium, .large, .height(80)], selection: $selectedDetent) .presentationCornerRadius(Constants.cornerRadius) .presentationBackgroundInteraction(.enabled) .interactiveDismissDisabled(true) } } } // 根据当前档位计算控件垂直偏移量 private func calculateOffset(for detent: PresentationDetent) -> CGFloat { switch detent { case .height(80): return -40 // Sheet处于最低位时的偏移 case .medium: return -200 // Sheet处于中间位时的偏移 case .large: return -400 // Sheet处于最高位时的偏移 default: return -40 } } }
方案二:实时监听Sheet拖动进度(精准跟随)
支持拖动Sheet过程中,控件实时同步位置:
- 创建监听Sheet进度的桥接视图:
struct SheetProgressView: UIViewControllerRepresentable { @Binding var progress: CGFloat func makeUIViewController(context: Context) -> UIViewController { let vc = UIViewController() vc.view.isHidden = true return vc } func updateUIViewController(_ uiViewController: UIViewController, context: Context) { guard let sheet = uiViewController.presentingViewController?.presentedViewController as? UISheetPresentationController else { return } sheet.delegate = context.coordinator } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UISheetPresentationControllerDelegate { let parent: SheetProgressView init(_ parent: SheetProgressView) { self.parent = parent } func sheetPresentationController(_ sheetPresentationController: UISheetPresentationController, didChangeScrollEdgeAppearance appearance: UIScrollEdgeAppearance?) { // 计算Sheet拖动进度(需根据屏幕尺寸调整逻辑) let screenHeight = UIScreen.main.bounds.height let sheetTopY = sheetPresentationController.presentedViewController.view.frame.minY parent.progress = 1 - (sheetTopY / screenHeight) } } }
- 在主视图中集成进度监听,同步控件位置:
struct ContentView: View { @State private var sheetProgress: CGFloat = 0 var body: some View { ZStack { HomeView() // 跟随控件组 HStack { Button(action: {}) { Image(systemName: "binoculars") .padding() .background(.white) .cornerRadius(12) } Spacer() Text("AQI 42 良") .padding(.horizontal, 16) .padding(.vertical, 8) .background(.white) .cornerRadius(12) } .padding(.horizontal, 16) .offset(y: -sheetProgress * 360) // 系数根据实际UI调整 .sheet(isPresented: .constant(true)) { ActivityView() .presentationDetents([.medium, .large, .height(80)]) .presentationCornerRadius(Constants.cornerRadius) .presentationBackgroundInteraction(.enabled) .interactiveDismissDisabled(true) .background(SheetProgressView(progress: $sheetProgress)) } } } }
注意事项
- 偏移量数值需根据你的UI设计微调,确保控件始终处于Sheet顶部的合理位置。
- 可给控件添加与Sheet一致的阴影效果,强化"附着"在Sheet上的视觉感受。
内容的提问来源于stack exchange,提问作者Jon
相关产品推荐
相关产品推荐

