SwiftUI中Switch选子视图时WrapperView的any View报错解决
问题描述
我定义了一个接收遵循View协议的子视图的WrapperView结构体:
struct WrapperView<C: View>: View { let childView: C init(_ childView: () -> (C)) { self.childView = childView() } var body: some View { childView } }
另外我有一个RealView结构体,其bodyView函数根据条件返回封装后的子视图:
struct TextAView: View { var body: some View { Text("A") } } struct TextBView: View { var body: some View { Text("B") } } struct TextCView: View { var body: some View { Text("C") } } struct RealView: View { var body: some View { bodyView() } private func bodyView() -> some View { let type = 1 var bodyChild: any View switch type { case 1: bodyChild = TextAView() case 2: bodyChild = TextBView() case 3: bodyChild = TextCView() default: bodyChild = EmptyView() } return WrapperView { //Text("Hello") <- 此处正常运行 bodyChild // <- 报错:Type 'any View' cannot conform to 'View' } } }
已知报错原因是bodyChild的类型为any View,但编译器要求传入具体的View类型。请问如何修改才能实现根据条件返回bodyChild并正常完成封装?
解决方案
方案1:直接在Switch分支返回WrapperView
不需要先存储any View类型的变量,直接在每个case分支中生成对应子视图并传入WrapperView,编译器会自动推导每个分支的具体泛型类型,最终通过some View统一返回:
struct RealView: View { var body: some View { bodyView() } private func bodyView() -> some View { let type = 1 switch type { case 1: return WrapperView { TextAView() } case 2: return WrapperView { TextBView() } case 3: return WrapperView { TextCView() } default: return WrapperView { EmptyView() } } } }
方案2:用AnyView做类型擦除
如果需要保留存储bodyChild的逻辑,可以用AnyView对any View进行类型擦除,将其转换为具体的View类型传入WrapperView:
struct RealView: View { var body: some View { bodyView() } private func bodyView() -> some View { let type = 1 var bodyChild: any View switch type { case 1: bodyChild = TextAView() case 2: bodyChild = TextBView() case 3: bodyChild = TextCView() default: bodyChild = EmptyView() } return WrapperView { AnyView(bodyChild) } } }
方案3:修改WrapperView支持any View
如果不需要依赖泛型的具体类型,可以直接修改WrapperView的定义,让它接受any View类型的子视图:
struct WrapperView: View { let childView: any View init(_ childView: () -> any View) { self.childView = childView() } var body: some View { childView } }
修改后原RealView的代码无需调整,直接传入bodyChild即可正常编译。
方案4:利用@ViewBuilder简化逻辑
使用@ViewBuilder属性包装器自动处理不同分支的视图类型差异,代码更简洁:
struct RealView: View { var body: some View { bodyView() } @ViewBuilder private func bodyView() -> some View { let type = 1 switch type { case 1: WrapperView { TextAView() } case 2: WrapperView { TextBView() } case 3: WrapperView { TextCView() } default: WrapperView { EmptyView() } } } }
内容的提问来源于stack exchange,提问作者Kunal Shah
相关产品推荐
相关产品推荐

