SwiftUI带泛型参数的容器视图Preview报错排查与解决
解决SwiftUI容器视图Preview报错及泛型约束问题
问题重现
我写了一个带泛型的SwiftUI容器视图,最初的简化版本如下:
public struct ContainerView<Content: View>: View { private let title: String private let mainView: Content public init(title: String, @ViewBuilder mainView: () -> Content) { self.title = title self.mainView = mainView() } public var body: some View { VStack { Text(title) mainView } } }
给它加#Preview时出现错误:Type of expression is ambiguous without a type annotation,预览代码如下:
#Preview { let mainView = { Text("some view") } return ContainerView(title: "title string", mainView: mainView) }
移除泛型<Content: View>相关代码后预览能正常运行,但实际我的代码里有两个视图参数:
public struct ContainerView<Content: View>: View { private let title: String private let mainView: Content private let secondaryView: Content public init(title: String, @ViewBuilder mainView: () -> Content, @ViewBuilder secondaryView: () -> Content) { self.title = title self.mainView = mainView() self.secondaryView = secondaryView() } public var body: some View { VStack { Text(title) mainView secondaryView } } }
当传入不同类型的视图(比如Text和Button)时,同样会触发类型错误:
#Preview { let mainView = { Text("some view") } let secondaryView = { Button(action: {}, label: { Text("Button example") }) } return ContainerView(title: "title string", mainView: mainView, secondaryView: secondaryView) }
问题原因
我原本以为只要两个视图都符合View协议就能正常工作,但Swift的泛型是具体类型绑定:这里的Content是单一泛型参数,要求mainView和secondaryView必须是完全相同的具体类型(比如都是Text,或者都是Button),而Text和Button是不同的具体类型,无法匹配同一个Content,导致编译器无法推断出Content的具体类型,从而抛出模糊类型的错误,Preview也因此失效。
解决方法
把容器视图的泛型声明改成两个独立的泛型参数,分别约束为View类型,这样两个子视图可以是任意不同的View类型:
public struct ContainerView<MainContent: View, SecondaryContent: View>: View { private let title: String private let mainView: MainContent private let secondaryView: SecondaryContent public init(title: String, @ViewBuilder mainView: () -> MainContent, @ViewBuilder secondaryView: () -> SecondaryContent) { self.title = title self.mainView = mainView() self.secondaryView = secondaryView() } public var body: some View { VStack { Text(title) mainView secondaryView } } }
修改后再运行之前的预览代码,就能正常显示预览,且支持传入任意不同类型的子视图。
内容的提问来源于stack exchange,提问作者invalidArgument
相关产品推荐
相关产品推荐

