【发布时间】:2020-09-24 01:59:03
【问题描述】:
我正在尝试使用 @EnvironmentObject 来控制我的应用程序的某些方面。我遇到的问题是我的一个控制器无法访问环境对象。我收到致命错误“未找到环境类型的 @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,而 NextDeparturesView 又调用 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,这是发生致命错误的地方
位置控制器
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结构中,而不是Locationvar,我现在有一个LocationController,因此在整个应用程序中使用相同的实例。在我的LocationController中,我现在有一个@Published var location: Location,因此每个视图都可以访问相同的位置。 - 在
View类型的结构中,我创建@EnvironmentObject var environmentController: EnvironmentController并使用与之关联的LocationController。在其他类类型中,我只是有一个init方法,它接收一个LocationController,它通过environmentController发送,例如,当我调用MapView时,我这样做:MapView(locController: environmentController.environment.locationController)从而确保它是整个应用程序中使用的相同控制器和正在更改的相同Location。在MapView等类中使用@ObservedObject var locationController: LocationController很重要,否则将无法检测到更改。
希望这会有所帮助。
【问题讨论】:
-
How to pass EnvironmentObject into View Model? 简短回答:您应该只在视图中使用
@EnvironmentObject。
标签: ios swift xcode environment-variables swiftui