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

SwiftUI视图添加首尾图标及置顶固定技术问询

解答方案

1. 添加首尾图标(关闭按钮 + Clear按钮)

SwiftUI没有内置这种带首尾操作按钮的顶部栏样式,需要通过自定义HStack实现。你可以在视图最顶部创建横向布局,分别放置左侧关闭图标、中间标题,以及右侧Clear按钮。

2. 顶部固定悬浮实现

要让顶部栏在内容滚动时保持悬浮,只需将顶部栏与滚动内容分开展示:用VStack包裹固定的顶部栏,下方用ScrollView承载可滚动的筛选内容即可。

3. 修改后的完整代码

struct CustomSheetView: View {
    @Environment(\.presentationMode) var presentationMode
    // iOS 15+ 可替换为 @Environment(\.dismiss) private var dismiss

    @State private var isNewInMenuSelected = false
    @State private var isOurFavoritesSelected = false
    @State private var isCustomerPickSelected = false

    var filtersLabel: String
    var useOurSearchFiltersLabel: String
    var applyFiltersLabel: String

    var body: some View {
        VStack(spacing: 0) {
            // 固定悬浮的顶部栏
            HStack {
                // 左侧关闭按钮
                Button(action: {
                    presentationMode.wrappedValue.dismiss()
                    // iOS15+ 用 dismiss()
                }) {
                    Image(systemName: "xmark")
                        .foregroundColor(.primary)
                        .padding()
                }

                Spacer()

                // 中间标题
                Text(filtersLabel)
                    .font(.largeTitle)
                    .fontWeight(.bold)

                Spacer()

                // 右侧Clear按钮
                Button(action: {
                    // 重置所有Toggle状态
                    isNewInMenuSelected = false
                    isOurFavoritesSelected = false
                    isCustomerPickSelected = false
                }) {
                    Text("Clear")
                        .foregroundColor(.blue)
                        .padding()
                }
            }
            .padding(.top, 20)
            .background(Color.white) // 背景色防止滚动内容穿透

            // 可滚动的内容区域
            ScrollView {
                VStack(alignment: .leading, spacing: 20) {
                    Text(useOurSearchFiltersLabel)
                        .font(.subheadline)
                        .fontWeight(.medium)
                        .foregroundColor(.secondary)
                        .padding(.leading, 20)
                        .padding(.top, 20)

                    VStack(alignment: .leading, spacing: 10) {
                        Toggle("New in menu", isOn: $isNewInMenuSelected)
                        Toggle("Our favorites", isOn: $isOurFavoritesSelected)
                        Toggle("Customer pick", isOn: $isCustomerPickSelected)
                    }
                    .padding(.horizontal, 20)

                    Spacer(minLength: 40)

                    Button(action: {
                        let checkboxStates = [
                            "new_in_menu": isNewInMenuSelected,
                            "our_favorites": isOurFavoritesSelected,
                            "customer_pick": isCustomerPickSelected
                        ]

                        let keyWindow = UIApplication.shared.connectedScenes
                            .filter { $0.activationState == .foregroundActive }
                            .compactMap { $0 as? UIWindowScene }
                            .flatMap { $0.windows }
                            .first { $0.isKeyWindow }

                        if let flutterViewController = keyWindow?.rootViewController as? FlutterViewController {
                            let channel = FlutterMethodChannel(name: "custom_sheet_channel", binaryMessenger: flutterViewController.binaryMessenger)
                            channel.invokeMethod("checkboxStates", arguments: checkboxStates) { result in
                                // Handle any response if necessary
                            }
                        }

                        presentationMode.wrappedValue.dismiss()
                    }) {
                        Text(applyFiltersLabel)
                            .padding()
                            .frame(maxWidth: .infinity)
                            .background(Color(hex: "#d0a771"))
                            .foregroundColor(.white)
                            .cornerRadius(8)
                            .padding(.horizontal, 20)
                    }
                    .padding(.bottom, 20)
                }
                .frame(maxWidth: .infinity, alignment: .topLeading)
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

// 扩展Color支持十六进制初始化(如果你的项目里还没有的话)
extension Color {
    init(hex: String) {
        let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int: UInt64 = 0
        Scanner(string: hex).scanHexInt64(&int)
        let a, r, g, b: UInt64
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            (a, r, g, b) = (1, 1, 1, 0)
        }

        self.init(
            .sRGB,
            red: Double(r) / 255,
            green: Double(g) / 255,
            blue: Double(b) / 255,
            opacity: Double(a) / 255
        )
    }
}

关键说明

  • 顶部栏使用HStack实现左右按钮+中间标题的布局,通过Spacer()自动分配空间,确保标题居中,按钮分别居左右。
  • 顶部栏设置背景色(这里用白色),避免滚动内容滚动到顶部栏下方时出现穿透效果。
  • 内容区域用ScrollView包裹,这样当筛选选项增多时可以滚动,而顶部栏始终固定在视图最上方。
  • Clear按钮的点击逻辑是重置所有Toggle的选中状态,你可以根据需求调整这个行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 13:07:27