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

SwiftUI中如何在UIViewRepresentable通过@Environment传入遵循CarItem协议的模型?

问题

以下代码尝试在UIViewRepresentable中通过@Environment获取遵循CarItem协议的模型,但@Environment(CarItem.self)报错No exact matches in call to initializer,想知道是否能实现该需求,以及具体操作方法:

protocol CarItem: AnyObject, Observable {
    var carName:String { get }
}


struct CarViewUIKRepresented: UIViewRepresentable {
    
    @Environment(CarItem.self) var item // <<<<<< ERROR: No exact matches in call to initializer 
    
    func makeUIView(context: Context) -> CarUIView {
        let carView = CarUIView()
        carView.setupWith(car:item)
        return carView
    }
    
    func updateUIView(_ uiView: CarUIView, context: Context) {
        
    }
    
}

class CarUIView : UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func setupWith<T:CarItem>(car:T) {
        ///
    }
    
}

解决方案

可以实现,SwiftUI的@Environment不能直接用协议类型初始化,需要通过自定义EnvironmentKey来实现协议类型的环境注入,具体步骤如下:

1. 自定义EnvironmentKey

创建遵循EnvironmentKey的结构体,指定默认值(因CarItem是AnyObject类型,默认值设为nil):

private struct CarItemKey: EnvironmentKey {
    static var defaultValue: CarItem? = nil
}

2. 扩展EnvironmentValues添加对应属性

给EnvironmentValues扩展一个属性,用来访问和设置CarItem实例:

extension EnvironmentValues {
    var carItem: CarItem? {
        get { self[CarItemKey.self] }
        set { self[CarItemKey.self] = newValue }
    }
}

3. 在视图层级中注入环境对象

先定义一个遵循CarItem的具体类,再在父视图中通过environment注入实例:

// 示例:遵循CarItem的具体类
class SedanCar: NSObject, CarItem {
    @Published var carName: String = "Sedan"
}

// 在父视图中注入环境对象
struct ContentView: View {
    let sedan = SedanCar()
    
    var body: some View {
        CarViewUIKRepresented()
            .environment(\.carItem, sedan)
    }
}

4. 修改UIViewRepresentable中的环境获取方式

将原来的协议类型初始化替换为自定义的环境属性:

struct CarViewUIKRepresented: UIViewRepresentable {
    
    @Environment(\.carItem) var item // 使用自定义环境key
    
    func makeUIView(context: Context) -> CarUIView {
        let carView = CarUIView()
        if let carItem = item {
            carView.setupWith(car: carItem)
        }
        return carView
    }
    
    func updateUIView(_ uiView: CarUIView, context: Context) {
        // 若需响应数据更新,可在此重新调用setupWith
        if let carItem = item {
            uiView.setupWith(car: carItem)
        }
    }
    
}

补充说明

  • 因CarItem是AnyObject类型,环境属性只能是可选值,使用时需做非空判断
  • 若CarItem的属性用@Published修饰,可在updateUIView中重新调用setupWith更新UI
  • CarUIView的泛型方法setupWith<T:CarItem>可直接接收CarItem实例,无需额外修改

内容的提问来源于stack exchange,提问作者zumzum

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:47:23