如何在SwiftUI视图中使用可观察对象类中的函数?
我试图创建一个位置数组作为一个可观察的对象变量,以便视图在数组发生更改时随时更新。正在另一个视图中添加/删除如何在SwiftUI视图中使用可观察对象类中的函数?,swift,swiftui,Swift,Swiftui,我试图创建一个位置数组作为一个可观察的对象变量,以便视图在数组发生更改时随时更新。正在另一个视图中添加/删除引脚中的数据。我已经创建了observable object类,如图所示,带有一个可识别的结构: struct Location: Identifiable { let id = UUID() let coordinate: CLLocationCoordinate2D let pinTitle: String let pinDescription: Str
引脚中的数据。我已经创建了observable object类,如图所示,带有一个可识别的结构:
struct Location: Identifiable {
let id = UUID()
let coordinate: CLLocationCoordinate2D
let pinTitle: String
let pinDescription: String
}
class Pins: ObservableObject {
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(sortDescriptors: []) private var pins: FetchedResults<Pin>
@Published var locations: [Location] = []
func loadPins() -> [Location] {
for pin in pins {
locations.append(Location(coordinate: CLLocationCoordinate2D(latitude: pin.pinLatitude, longitude: pin.pinLongitude), pinTitle: pin.pinTitle ?? "error", pinDescription: pin.pinDescription ?? "error"))
}
return locations
}
}
理想情况下,当从另一个视图对可观察对象数组进行添加/删除时,可观察对象应保持自动更新,但我甚至无法对其进行初始化。任何帮助都将不胜感激
解决方案:
解决这个问题的办法是提取核心数据。@FetchRequest属性包装器仅用于视图内部。下面的代码总结了该方法。此解决方案的所有功劳都归于。这篇博文的链接是。我已包含代码以供快速参考:
@main
struct MyApp: App {
let persistenceManager: PersistenceManager
@StateObject var pinItemStorage: PinItemStorage
init() {
let manager = PersistenceManager()
self.persistenceManager = manager
let managedObjectContext = manager.persistentContainer.viewContext
let storage = PinItemStorage(managedObjectContext: managedObjectContext)
self._pinItemStorage = StateObject(wrappedValue: storage)
}
var body: some Scene {
WindowGroup {
MotherView(pinItemStorage: pinItemStorage)
}
}
}
class PersistenceManager {
let persistentContainer: NSPersistentContainer = {
// name is name of core data data model file
let container = NSPersistentContainer(name: "MyAppModel")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
}
extension Pin {
static var dueSoonFetchRequest: NSFetchRequest<Pin> {
let request: NSFetchRequest<Pin> = Pin.fetchRequest()
request.sortDescriptors = []
return request
}
}
class PinItemStorage: NSObject, ObservableObject {
@Published var dueSoon: [Pin] = []
private let dueSoonController: NSFetchedResultsController<Pin>
init(managedObjectContext: NSManagedObjectContext) {
dueSoonController = NSFetchedResultsController(fetchRequest: Pin.dueSoonFetchRequest,
managedObjectContext: managedObjectContext,
sectionNameKeyPath: nil, cacheName: nil)
super.init()
dueSoonController.delegate = self
do {
try dueSoonController.performFetch()
dueSoon = dueSoonController.fetchedObjects ?? []
} catch {
print("failed to fetch items!")
}
}
}
extension PinItemStorage: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard let pins = controller.fetchedObjects as? [Pin]
else { return }
dueSoon = pins
}
}
从CoreData获取后,需要包装MKPointAnnotations&Pins,如下所示:
import MapKit
extension MKPointAnnotation: ObservableObject{
public var wrappedTitle: String{
get{
self.pinTitle ?? "No Title"
}
set{
self.pinTitle = newValue
}
}
public var wrappedSubtitle: String{
get{
self.pinDescription ?? "No information on this location"
}
set{
self.pinDescription = newValue
}
}
}
您还可以尝试将以下函数添加到Location类中
import SwiftUI
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
let objectWillChange = PassthroughSubject<Void, Never>()
private let geocoder = CLGeocoder()
@Published var status: CLAuthorizationStatus? {
willSet { objectWillChange.send() }
}
@Published var location: CLLocation? {
willSet { objectWillChange.send() }
}
@Published var placemark: CLPlacemark? {
willSet { objectWillChange.send() }
}
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
private func geocode() {
guard let location = self.location else { return }
geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
if error == nil {
self.placemark = places?[0]
} else {
self.placemark = nil
}
})
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.status = status
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
self.location = location
self.geocode()
}
}
extension CLLocation {
var latitude: Double {
return self.coordinate.latitude
}
var longitude: Double {
return self.coordinate.longitude
}
}
导入快捷界面
导入核心定位
进口联合收割机
类位置管理器:NSObject,observeObject{
私有let locationManager=CLLocationManager()
let objectWillChange=PassthroughSubject()
私有let geocoder=CLGeocoder()
@已发布变量状态:CLAuthorizationStatus{
willSet{objectWillChange.send()}
}
@发布的变量位置:CLLocation{
willSet{objectWillChange.send()}
}
@已发布变量placemark:CLPlacemark{
willSet{objectWillChange.send()}
}
重写init(){
super.init()
self.locationManager.delegate=self
self.locationManager.desiredAccuracy=KCallocationAccuracyBest
self.locationManager.requestWhenUseAuthorization()
self.locationManager.startUpdatingLocation()
}
专用func地理代码(){
guard let location=self.location else{return}
geocoder.reverseGeocodeLocation(位置,completionHandler:{(位置,错误)在
如果错误==nil{
self.placemark=位置?[0]
}否则{
self.placemark=nil
}
})
}
}
扩展位置管理器:CLLocationManagerDelegate{
func locationManager(\ manager:CLLocationManager,didChangeAuthorization状态:CLAuthorizationStatus){
self.status=状态
}
func locationManager(manager:CLLocationManager,didUpdateLocations位置:[CLLocation]){
guard let location=locations.last else{return}
self.location=位置
self.geocode()
}
}
分机定位{
纬度:双{
返回自坐标纬度
}
经度:双{
返回self.coordinate.longitude
}
}
我猜@FetchRequest在调用loadPins
时没有实际执行。如果在.onReceive(pinLocations.$pins)
上更改onAppear
,会发生什么情况?FetchRequest用于视图内部,而不是对象内部。但就个人而言,我认为这是一个反模式API,因为现在(a)您的视图直接访问您的模型(b)他们解释原始存储模型(!!)和(c)您的应用程序的所有内容都与CoreData关联。如果您希望运行Firebase以实现跨平台实现,该怎么办?你必须改变一切。最佳实践是将数据库抽象出来,让服务来处理它。如果你刚开始观看CS193课程以获得这种模式,这是课程中令人失望的一部分。@qsaluan我喜欢这个博客作者/工程师给你的建议。如果它对您有效,请回来用词汇回答您自己的问题,这将有助于其他学习者了解该主题。让视图决定何时获取视图所依赖的数据。将@FetchRequest移动到视图中,请参考CoreData示例项目。Xcode>newproject>选中CoreData框并查看示例
import MapKit
extension MKPointAnnotation: ObservableObject{
public var wrappedTitle: String{
get{
self.pinTitle ?? "No Title"
}
set{
self.pinTitle = newValue
}
}
public var wrappedSubtitle: String{
get{
self.pinDescription ?? "No information on this location"
}
set{
self.pinDescription = newValue
}
}
}
import SwiftUI
import CoreLocation
import Combine
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
let objectWillChange = PassthroughSubject<Void, Never>()
private let geocoder = CLGeocoder()
@Published var status: CLAuthorizationStatus? {
willSet { objectWillChange.send() }
}
@Published var location: CLLocation? {
willSet { objectWillChange.send() }
}
@Published var placemark: CLPlacemark? {
willSet { objectWillChange.send() }
}
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
private func geocode() {
guard let location = self.location else { return }
geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
if error == nil {
self.placemark = places?[0]
} else {
self.placemark = nil
}
})
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.status = status
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
self.location = location
self.geocode()
}
}
extension CLLocation {
var latitude: Double {
return self.coordinate.latitude
}
var longitude: Double {
return self.coordinate.longitude
}
}