如何基于@ViewBuilder实现支持枚举if case的View扩展方法?
实现支持
if case的View扩展方法 问题描述
已有一个根据布尔条件动态修改View的扩展方法:
/// Applies the given transform if the given condition evaluates to `true`. /// - Parameters: /// - condition: The condition to evaluate. /// - transform: The transform to apply to the source `View`. /// - Returns: Either the original `View` or the modified `View` if the condition is `true`. @ViewBuilder func `if`<Content: View>(_ condition: Bool, transform: (Self) -> Content) -> some View { if condition { transform(self) } else { self } }
希望将其适配为支持if case模式匹配的版本,用来处理带关联值的枚举,期望调用方式如下(注:该写法不符合Swift语法,需调整):
Text("Hello world") .ifCase(myEnumVar = .MyEnumCase(_), transform: { view view.foregroundColor(Color.red) })
解决方案
首先明确:Swift的模式匹配语法要求case 模式 = 值的顺序,不能颠倒为值 = 模式,所以调用时需要调整条件写法。以下提供两种实现方案:
方案1:基础版(仅匹配枚举Case)
实现一个扩展,接受通过case模式匹配得到的布尔条件,完成视图转换:
extension View { /// 当满足指定模式匹配条件时应用视图转换 /// - Parameters: /// - condition: 模式匹配条件(需返回Bool值) /// - transform: 匹配成功时的视图转换闭包 /// - Returns: 转换后的视图或原视图 @ViewBuilder func ifCase<Content: View>( _ condition: @autoclosure () -> Bool, transform: (Self) -> Content ) -> some View { if condition() { transform(self) } else { self } } }
调用示例:
// 先定义带关联值的枚举 enum MyEnum { case MyEnumCase(String) case otherCase } // 示例枚举值 let myEnumVar: MyEnum = .MyEnumCase("test") // 使用扩展修改视图 Text("Hello world") .ifCase(case .MyEnumCase(_) = myEnumVar) { view in view.foregroundColor(.red) }
方案2:进阶版(支持获取关联值)
如果需要在视图转换中使用枚举的关联值,可以实现带关联值提取的版本:
extension View { /// 当枚举值匹配指定带关联值的case时,应用带关联值参数的视图转换 /// - Parameters: /// - enumValue: 要匹配的枚举值 /// - extract: 用于提取关联值的闭包(匹配成功返回关联值,否则返回nil) /// - transform: 匹配成功时的视图转换闭包,可接收原视图和关联值 /// - Returns: 转换后的视图或原视图 @ViewBuilder func ifCase<Enum, AssociatedValue, Content: View>( _ enumValue: Enum, extract: (Enum) -> AssociatedValue?, transform: (Self, AssociatedValue) -> Content ) -> some View { if let value = extract(enumValue) { transform(self, value) } else { self } } }
调用示例:
Text("Hello world") .ifCase(myEnumVar, extract: { // 匹配指定case并提取关联值 guard case let .MyEnumCase(text) = $0 else { return nil } return text }) { view, associatedText in // 使用关联值修改视图 view.foregroundColor(.red) .overlay(Text("关联值:\(associatedText)").font(.caption)) }
内容的提问来源于stack exchange,提问作者xarly
相关产品推荐
相关产品推荐

