You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在协议返回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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 14:47:37