我正在尝试使用@EnvironmentObject来控制我的应用程序的某些方面。我遇到的问题是我的一个控制器无法访问环境对象。我收到致命错误“找不到类型为Environment的@ObservableObject”。
我搜索了其他问题,发现的每个解决方案都包括将.environmentObject(myEnvironment)
发送到相关视图。问题是这不是一个视图,我似乎没有那个选择。
另外,在我的SceneDelegate中,我将environmentObject发送到第一个视图,所以这不是问题。
这是我的代码。
首先,我创建一个模型来声明我的所有环境变量
环境
struct Environment {
var showMenu: Bool
var searchText: String
var location : Location
init() {
self.showMenu = false
self.searchText = ""
self.location = Location()
}
}
接下来,我有一个控制器,其目的是处理与环境有关的所有操作,现在它没有
环境控制器
import Foundation
class EnvironmentController : ObservableObject {
@Published var environment = Environment()
}
现在,在SceneDelegate中,我调用NextDeparturesView,后者依次调用MapView。
MapView
import SwiftUI
import MapKit
//MARK: Map View
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 {
//MARK: Vars
@EnvironmentObject var environmentController: EnvironmentController
@ObservedObject var userSettingsController = UserSettingsController()
//var declaration - Irrelevant code to the question
//MARK: Location Manager
var locationManager = CLLocationManager()
//MARK: Init
override init() {
//more irrelevant code
super.init()
//Ask for location access
self.updateLocation()
}
//MARK: Functions
func updateLocation() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
if locationManager.responds(to: #selector(CLLocationManager.requestAlwaysAuthorization)){
locationManager.requestAlwaysAuthorization()
}
else {
locationManager.startUpdatingLocation()
}
}
//MARK: CLLocationManagerDelegate methods
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()
}
//MARK: Other Functions
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的大量帮助后,我解决了我的问题,但是对应用程序的整体结构进行了一些更改。
我会接受他的答案是正确的,但是我会提供一份我所做的事情的清单,以便将来看到此问题的人都可以向正确的方向轻推。
View
和UIViewRepresentable
都可以访问@EnvironmentObject
。只有View
可以。 Environment
结构中,现在有了Location
,而不是LocationController
var,因此在整个应用程序中使用了相同的实例。在我的LocationController
中,我现在有一个@Published var location: Location
,因此每个视图都可以访问相同的位置。 View
类型的结构中,我创建@EnvironmentObject var environmentController: EnvironmentController
并使用与之关联的LocationController
。在其他类类型中,我只是有一个init
方法,该方法接收一个LocationController
,该方法是通过environmentController
发送的,例如,当我调用MapView
时,我会执行:MapView(locController: environmentController.environment.locationController)
,从而确保它是整个应用程序中使用的同一控制器以及相同的Location
正在改变。在@ObservedObject var locationController: LocationController
之类的类中使用MapView
很重要,否则将不会检测到更改。 希望这可以帮助。
最佳答案
请勿在@EnvironmentObject
/ Controller
中使用ViewModel
(实际上是View
之外的任何地方)。如果要观察Controller中环境的变化,可以执行以下操作:
class Environment: ObservableObject {
@Published var showMenu: Bool = false
@Published var searchText: String = ""
@Published var location : Location = Location()
}
class Controller: ObservableObject {
@Published var showMenu: Bool
private var environment: Environment
private var cancellables = Set<AnyCancellable>()
init(environment: Environment) {
_showMenu = .init(initialValue: environment.showMenu)
environment.$showMenu
.receive(on: DispatchQueue.main)
.sink(receiveValue: { [weak self] value in
self?.showMenu = value
})
.store(in: &cancellables)
}
}
您还可以使用其他形式的依赖注入来注入
Environment
(甚至使用单例)。通常,有多种方法可以在视图中显示
Environment
变量(例如showMenu
)(并刷新):1)
Environment
作为View
注入到您的ViewModel
中(而不是)作为@EnvironmentObject
-在仅需要从Environment
访问View
的情况下。2)
ViewModel
订阅Environment
(如上所述),并将其自己的变量发布到View
。然后,无需在@EnvironmentObject
中使用View
。3)
Environment
作为View
注入到@EnvironmentObject
中,然后传递给ViewModel
。关于ios - 无法在 Controller 中获取@EnvironmentObject,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/62205556/