iOS14.7中SwiftUI动态高度Sheet实现及日期选择器居中问题
iOS14.7 SwiftUI 动态高度Sheet日期时间选择器居中解决方案
核心修改点
- 移除Sheet调用中的固定高度
frame(height:580),让Sheet高度由内部内容自适应 - 给图形化日期选择器设置
frame(maxWidth: .infinity),确保它在容器内居中显示 - 实现自定义导航栏与底部操作按钮的布局,适配动态高度需求
完整代码实现
Sheet调用修改
.sheet(isPresented: $showEditDataTime) { EditDateAndTimeView() .clearModalBackground() }
EditDateAndTimeView 实现
struct EditDateAndTimeView: View { @Environment(\.dismiss) private var dismiss @State private var selectedDate = Date() var body: some View { VStack(spacing: 0) { // 自定义导航栏 HStack { Button("取消") { dismiss() } .foregroundColor(.blue) Spacer() Text("选择日期时间") .font(.headline) Spacer() Button("确定") { dismiss() // 这里添加选中日期的处理逻辑 } .foregroundColor(.blue) } .padding(.horizontal, 16) .padding(.vertical, 12) .border(.separator, edge: .bottom) // 居中的图形化日期时间选择器 DatePicker("", selection: $selectedDate, displayedComponents: [.date, .hourAndMinute]) .datePickerStyle(.graphical) .padding() .frame(maxWidth: .infinity) .background(Color(.systemBackground)) Spacer() // 底部操作按钮组 HStack(spacing: 16) { Button("重置") { selectedDate = Date() } .frame(maxWidth: .infinity) .padding() .background(Color(.systemGray5)) .cornerRadius(8) Button("应用") { dismiss() // 这里添加应用日期的处理逻辑 } .frame(maxWidth: .infinity) .padding() .background(Color.blue) .foregroundColor(.white) .cornerRadius(8) } .padding(.horizontal, 16) .padding(.bottom, 20) } .background(Color(.systemGroupedBackground)) } }
辅助扩展(兼容iOS14.7)
// 清除Sheet背景透明的扩展 extension View { func clearModalBackground() -> some View { if #available(iOS 15.0, *) { return self.presentationBackground(.clear) } else { return self .background(Color.clear) .onAppear { guard let window = UIApplication.shared.windows.first else { return } window.rootViewController?.presentedViewController?.view.backgroundColor = .clear } } } } // 给视图添加单边边框的扩展 extension View { func border(_ color: Color, edge: Edge, lineWidth: CGFloat = 1) -> some View { overlay(EdgeBorder(color: color, edge: edge, lineWidth: lineWidth)) } } struct EdgeBorder: Shape { var color: Color var edge: Edge var lineWidth: CGFloat func path(in rect: CGRect) -> Path { var path = Path() switch edge { case .top: path.move(to: CGPoint(x: rect.minX, y: rect.minY)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.minY)) case .bottom: path.move(to: CGPoint(x: rect.minX, y: rect.maxY)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) case .leading: path.move(to: CGPoint(x: rect.minX, y: rect.minY)) path.addLine(to: CGPoint(x: rect.minX, y: rect.maxY)) case .trailing: path.move(to: CGPoint(x: rect.maxX, y: rect.minY)) path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY)) } return path.strokedPath(.init(lineWidth: lineWidth)) } }
关键说明
- 日期选择器居中:通过
frame(maxWidth: .infinity)让选择器占满父容器的可用宽度,配合padding()保证左右边距,实现居中效果。 - 动态高度Sheet:移除固定高度后,Sheet会根据内部VStack的内容自动计算高度,Spacer会将底部按钮推至Sheet底部。
- iOS14.7兼容:针对iOS14的Sheet背景透明处理使用了UIKit的API,自定义导航栏的单边边框通过Shape实现,避免使用iOS15+的专属API。
内容的提问来源于stack exchange,提问作者Lce
相关产品推荐
相关产品推荐

