SwiftUI如何过滤ViewBuilder子视图?仿.swipeActions实现自定义视图
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
相关产品推荐
相关产品推荐

