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
相关产品推荐
相关产品推荐

