无法在控制器中获取@EnvironmentObject。
无法在控制器中获取@EnvironmentObject。
我试图使用@EnvironmentObject来控制我的应用程序的某些方面。我遇到的问题是,我的一个控制器无法访问环境对象。我得到了致命错误“找不到Environment类型的@ObservableObject”。
我搜索了其他问题,我找到的每个解决方案都包括在相关视图中发送.environmentObject(myEnvironment)
。问题是这不是一个视图,我似乎没有这个选项。
此外,在我的SceneDelegate中,我将environmentObject发送给第一个视图,所以那不是问题。
以下是我的代码。
首先,我创建了一个模型来声明所有的环境变量
Environment
struct Environment {
var showMenu: Bool
var searchText: String
var location : Location
init() {
self.showMenu = false
self.searchText = ""
self.location = Location()
}
}
接下来,我有一个控制器,它的目的是处理与环境相关的任何操作,现在它没有任何操作
EnvironmentController
import Foundation
class EnvironmentController : ObservableObject {
@Published var environment = Environment()
}
现在,在SceneDelegate中,我调用NextDeparturesView,然后调用MapView。
MapView
import SwiftUI
import MapKit
struct MapView : UIViewRepresentable {
@EnvironmentObject var environmentController: EnvironmentController
var locationController = LocationController()
func makeUIView(context: Context) -> MKMapView {
MKMapView(frame: .zero)
}
func updateUIView(_ uiView: MKMapView, context: Context) {
let coordinate = CLLocationCoordinate2D(
latitude: environmentController.environment.location.latitude,
longitude: environmentController.environment.location.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
let region = MKCoordinateRegion(center: coordinate, span: span)
uiView.showsUserLocation = true
uiView.setRegion(region, animated: true)
}
}
你会注意到,在MapView中,我调用了LocationController,这就是致命错误发生的地方。
LocationController
import SwiftUI
import MapKit
import CoreLocation
final class LocationController: NSObject, CLLocationManagerDelegate, ObservableObject {
@EnvironmentObject var environmentController: EnvironmentController
@ObservedObject var userSettingsController = UserSettingsController()
var locationManager = CLLocationManager()
override init() {
super.init()
self.updateLocation()
}
func updateLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if locationManager.responds(to: #selector(CLLocationManager.requestAlwaysAuthorization)){
locationManager.requestAlwaysAuthorization()
}
else {
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Error updating location :%@", error)
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
self.setDefaultLocation()
break
case .restricted:
self.setDefaultLocation()
break
case .denied:
self.setDefaultLocation()
break
default:
locationManager.startUpdatingLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let currentLocation = manager.location?.coordinate
self.environmentController.environment.location.latitude = Double(currentLocation!.latitude)
self.environmentController.environment.location.longitude = Double(currentLocation!.longitude)
manager.stopUpdatingLocation()
}
func recenter() {
locationManager.startUpdatingLocation()
}
func setDefaultLocation() {
if self.$userSettingsController.userCity.wrappedValue == "" {
self.environmentController.environment.location.latitude = 0.0
self.environmentController.environment.location.longitude = 0.0
} else {
self.environmentController.environment.location.latitude = self.citiesDictionary[self.userSettingsController.userCity]!.latitude
self.environmentController.environment.location.longitude = self.citiesDictionary[self.userSettingsController.userCity]!.longitude
}
}
}
所以,这就是致命错误发生的地方。例如,我的应用程序通常首先调用`setDefaultLocation()`,并且应用程序在那里崩溃。你有什么想法我做错了什么,或者如何解决这个问题?
提前谢谢你的帮助。
编辑
在得到了@pawello2222的很多帮助后,我解决了我的问题,但是对整个应用程序的结构进行了一些更改。
我将接受他的答案作为正确答案,但是我将提供我所做的事情的列表,这样以后看到这个问题的人可能会朝正确的方向前进。
- 我错误地认为
和 都可以访问<@EnvironmentObject>。只有 才能。 - 在我的
结构中,我现在有一个 而不是<@Published var location: Location>,因此在整个应用程序中使用相同的实例。在我的 中,我现在有一个<@Published var location: Location>,所以每个视图都可以访问相同的位置。 - 在
类型的结构中,我创建<@EnvironmentObject var environmentController: EnvironmentController>并使用与之关联的 。在其他类类型中,我简单地有一个 方法接收 ,这通过 发送,例如当我调用 时,我这样做: ,确保在整个应用程序中使用的是相同的控制器和相同的 正在被更改。重要的是,在类如 中使用<@ObservedObject var locationController: LocationController>,否则不会检测到更改。
希望对你有所帮助。
无法在控制器中获取@EnvironmentObject。
问题的原因是不能在Controller/ViewModel(实际上在View之外的任何地方)中使用。如果要在控制器中观察环境的变化,可以这样做:
class Environment: ObservableObject {
@Published var showMenu: Bool = false
@Published var searchText: String = ""
@Published var location: Location = Location()
}
class Controller: ObservableObject {
@Published var showMenu: Bool = false
private var cancellables = Set
init(environment: Environment) {
environment.$showMenu
.receive(on: DispatchQueue.main)
.assign(to: \.showMenu, on: self)
.store(in: &cancellables)
}
}
你也可以使用其他形式的依赖注入来注入Environment(甚至使用单例)。
通常有不同的方法来显示View中的Environment变量(例如showMenu)并刷新它:
1. 将Environment作为环境对象注入到View中(而不是ViewModel),作为一个@EnvironmentObject - 用于在View中访问Environment的情况。
2. ViewModel订阅Environment,并将自己的变量发布给View。在这种情况下,不需要在View中使用@EnvironmentObject。
3. 将Environment作为环境对象注入到View中,并传递给ViewModel。
如果在调用LocationController的视图(MapView)中没有该对象,则无法访问环境对象。例如,我的入口点视图具有该对象,我是否可以简单地调用MapView()?还是必须调用MapView().environmentObject(envObject)?
谢谢。我正在尝试执行第3个选项。我编辑了我的问题以显示发生了什么。希望你能帮助我。
UIViewRepresentable与View不同。尝试在init中将Environment传递给你的MapView。类似这样:MapView(environmentController: environmentController)。
我已经尝试了这个,但是我仍然无法将参数分配给environmentController或locationController。我必须删除符号吗?我更新了代码,你可以看到我的意思。
不能在View之外使用属性包装器。从environmentController的声明中移除
,问题应该就解决了。
让我们在聊天中继续讨论。