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

SwiftUI如何过滤ViewBuilder子视图?仿.swipeActions实现自定义视图

实现类似List.swipeActions的自定义ViewModifier(仅保留Button)

List的.swipeActions接受ViewBuilder作为参数,但会自动忽略其中的非Button视图。要实现相同逻辑的自定义ViewModifier,可按以下步骤操作:

1. 定义按钮数据模型

先创建一个模型来存储按钮的核心信息,方便后续统一处理:

struct SwipeActionButton {
    let action: () -> Void
    let label: String
    let backgroundColor: Color?
}

2. 实现自定义ViewModifier

这个Modifier会接收ViewBuilder内容,解析并过滤出其中的Button,再将这些按钮应用到目标视图上(示例中用右侧叠加按钮模拟侧滑效果,你可根据需求调整交互逻辑):

struct CustomSwipeActionsModifier: ViewModifier {
    @State private var swipeButtons: [SwipeActionButton] = []
    let content: () -> View

    func body(content: Content) -> some View {
        content
            .overlay(alignment: .trailing) {
                HStack(spacing: 0) {
                    ForEach(swipeButtons.indices, id: \.self) { index in
                        Button(swipeButtons[index].label) {
                            swipeButtons[index].action()
                        }
                        .frame(width: 80, height: .infinity)
                        .background(swipeButtons[index].backgroundColor ?? .red)
                        .foregroundColor(.white)
                    }
                }
            }
            .onAppear {
                // 遍历ViewBuilder中的子视图,筛选Button
                let viewTree = _VariadicView.Tree(content)
                swipeButtons = viewTree.children.compactMap { child in
                    guard let button = child.view as? Button<Text> else {
                        return nil // 忽略非Button视图
                    }
                    // 提取Button的标签文本
                    let labelText = (button.label as? Text)?.storage.description ?? ""
                    // 提取自定义样式中的背景色
                    let bgColor = button.backgroundColor
                    
                    return SwipeActionButton(
                        action: button.action,
                        label: labelText,
                        backgroundColor: bgColor
                    )
                }
            }
    }
}

// 扩展Button,用于提取背景色(通过自定义ButtonStyle实现)
extension Button {
    var backgroundColor: Color? {
        let mirror = Mirror(reflecting: self)
        for child in mirror.children {
            if let style = child.value as? PrimitiveButtonStyle {
                return style.backgroundColor
            }
        }
        return nil
    }
}

// 自定义ButtonStyle,用于存储背景色
struct ColoredButtonStyle: PrimitiveButtonStyle {
    let backgroundColor: Color
    
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(backgroundColor)
            .foregroundColor(.white)
            .onTapGesture {
                configuration.trigger()
            }
    }
}

// 为PrimitiveButtonStyle添加背景色属性的默认实现
extension PrimitiveButtonStyle {
    var backgroundColor: Color? { nil }
}

// 让自定义ColoredButtonStyle返回存储的背景色
extension ColoredButtonStyle {
    var backgroundColor: Color? { backgroundColor }
}

3. 添加View扩展,简化调用

给View添加扩展,让自定义侧滑按钮的调用方式和原生.swipeActions一致:

extension View {
    func customSwipeActions(@ViewBuilder content: @escaping () -> View) -> some View {
        modifier(CustomSwipeActionsModifier(content: content))
    }
}

4. 使用示例

调用时和原生API几乎一致,非Button视图会被自动忽略:

struct ContentView: View {
    var body: some View {
        List {
            Text("测试条目")
                .customSwipeActions {
                    Button("删除") {
                        print("执行删除操作")
                    }
                    .buttonStyle(ColoredButtonStyle(backgroundColor: .red))
                    
                    Text("此视图会被忽略") // 非Button,自动过滤
                    
                    Button("收藏") {
                        print("执行收藏操作")
                    }
                    .buttonStyle(ColoredButtonStyle(backgroundColor: .orange))
                }
        }
    }
}

核心逻辑说明

  • 利用_VariadicView.Tree遍历ViewBuilder中的所有子视图,这是SwiftUI内置的可变视图集合处理工具。
  • 通过类型检查筛选出Button视图,非Button类型直接返回nil,实现自动忽略效果。
  • 示例中的侧滑效果是基础实现,你可以进一步添加手势识别、滑动动画等,让交互更贴近原生.swipeActions的体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 20:07:49