如何在协议返回View时不使用AnyView?
不使用AnyView实现需求的方案
要避免使用AnyView,我们可以通过关联类型+类型擦除的方式解决协议关联类型带来的编译问题,具体实现如下:
1. 改造Thing协议,添加关联类型
将返回AnyView的属性改为返回关联类型的视图生成方法,让每个实现类可以返回具体的some View:
protocol Thing: AnyObject { associatedtype MenuView: View var name: String { get } func makeMenuItems() -> MenuView }
2. 实现类型擦除容器
创建一个ErasedThing结构体,用于擦除不同Thing实现的关联类型差异,统一对外接口:
struct ErasedThing: Identifiable { let id: String private let _makeMenuItems: () -> any View init<T: Thing>(_ thing: T) { self.id = thing.name self._makeMenuItems = { thing.makeMenuItems() } } func makeMenuItems() -> any View { _makeMenuItems() } }
3. 修改Thing的实现类
让Movie和CookieJar直接返回具体视图,无需包装AnyView:
class Movie: Thing { var name = "Movie" func makeMenuItems() -> some View { Button(action: {}) { Text("🎬") } } } class CookieJar: Thing { var name = "Cookie Jar" func makeMenuItems() -> some View { Button(action: {}) { Text("🍪") } } }
4. 调整ContentView
将数组类型改为[ErasedThing],利用Identifiable特性简化ForEach的使用:
struct ContentView: View { var things: [ErasedThing] var body: some View { ForEach(things) { thing in Text(thing.id) .contextMenu { thing.makeMenuItems() } } } } #Preview { ContentView(things: [ErasedThing(Movie()), ErasedThing(CookieJar())]) }
为什么之前用associatedtype会报错?
带有关联类型的协议属于泛型协议,无法直接作为存在类型(any Thing)存入数组——编译器无法确定数组中每个元素的关联类型具体是什么。通过ErasedThing类型擦除,我们把不同关联类型的Thing实例统一成了同一个类型,解决了类型不一致的问题。
内容的提问来源于stack exchange,提问作者swity learner
相关产品推荐
相关产品推荐

