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

如何用SwiftUI实现苹果健康APP同款悬浮滚轮日期选择器?

复刻苹果健康APP表单的简化方案

一、基础表单结构简化

别再手动写HStack+Text+Spacer了,SwiftUI原生的LabeledContent就是专门干这个的,配合Form和Section,自动实现标签左对齐、值右对齐的布局,直接砍掉一半冗余代码:

struct HealthFormView: View {
    @Environment(\.editMode) private var editMode
    @State private var selectedDate = Date()
    @State private var showDatePicker = false
    
    var body: some View {
        NavigationStack {
            Form {
                Section {
                    // 替代手写布局的LabeledContent
                    LabeledContent("出生日期") {
                        if editMode?.wrappedValue == .active {
                            Button(action: { showDatePicker = true }) {
                                Text(selectedDate.formatted(date: .long, time: .omitted))
                                    .foregroundColor(.blue)
                            }
                        } else {
                            Text(selectedDate.formatted(date: .long, time: .omitted))
                        }
                    }
                    
                    // 其他字段同理
                    LabeledContent("身高") {
                        if editMode?.wrappedValue == .active {
                            Button("175cm") { /* 处理身高编辑逻辑 */ }
                                .foregroundColor(.blue)
                        } else {
                            Text("175cm")
                        }
                    }
                }
            }
            .navigationTitle("个人信息")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    EditButton() // 原生Edit按钮,自动切换编辑模式
                }
            }
            .sheet(isPresented: $showDatePicker) {
                DatePickerView(selectedDate: $selectedDate, isPresented: $showDatePicker)
            }
        }
    }
}

二、批量处理Edit模式切换

如果字段多,每个都写if editMode判断太繁琐,封装一个复用组件就行:

struct EditToggleRow<Content: View>: View {
    let label: String
    let content: Content
    let action: () -> Void
    @Environment(\.editMode) private var editMode
    
    init(label: String, @ViewBuilder content: () -> Content, action: @escaping () -> Void) {
        self.label = label
        self.content = content()
        self.action = action
    }
    
    var body: some View {
        LabeledContent(label) {
            if editMode?.wrappedValue == .active {
                Button(action: action) {
                    content
                        .foregroundColor(.blue)
                }
            } else {
                content
            }
        }
    }
}

// 使用时一行搞定
EditToggleRow(label: "出生日期", content: {
    Text(selectedDate.formatted(date: .long, time: .omitted))
}) {
    showDatePicker = true
}

三、DatePicker弹窗样式修复(iOS 16+)

要实现健康APP那种只能通过Cancel/Done关闭、深色无圆角底部弹窗,用这几个关键API就能搞定:

struct DatePickerView: View {
    @Binding var selectedDate: Date
    @Binding var isPresented: Bool
    
    var body: some View {
        VStack(spacing: 0) {
            // 顶部操作栏
            HStack {
                Button("Cancel") {
                    isPresented = false
                }
                Spacer()
                Button("Done") {
                    isPresented = false
                }
                .bold()
            }
            .padding(.horizontal)
            .padding(.vertical, 12)
            .background(Color(.systemGray6))
            
            // 滚轮式选择器
            DatePicker("", selection: $selectedDate, displayedComponents: .date)
                .datePickerStyle(.wheel)
                .padding()
        }
        .presentationDetents([.medium]) // 固定高度的底部弹窗
        .presentationDragIndicator(.hidden) // 隐藏默认拖动条
        .interactiveDismissDisabled() // 禁止点击外部关闭,必须点按钮
        .background(Color(.systemBackground))
        .onAppear {
            // 强制去掉弹窗圆角
            if let sheetController = UIApplication.shared.windows.first?.rootViewController?.presentedViewController as? UISheetPresentationController {
                sheetController.preferredCornerRadius = 0
            }
        }
    }
}

四、你之前代码复杂的原因

你手动实现了原生组件已经支持的布局,又单独处理每个字段的编辑模式切换,没有做组件复用,导致代码冗余。上面的方案用原生API替代手写布局,把重复逻辑封装成组件,代码量至少减少三分之一。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 21:53:20