首页 > 解决方案 > 无法从用户位置 SwiftUi 更新 CLLocation()

问题描述

您好,我对 iOS 很陌生,SwiftUi所以如果我的格式错误,请不要恨我哈哈。我无法将用户位置坐标转换为美国地址。我已经完成了大部分代码,但由于某种原因在线109创建@Published var location并将其设置到某个随机位置,然后我尝试在线更新它,l72但由于某种原因它只使用我在线传递的第一个位置109。我喜欢在用户授予权限后更新和使用用户当前位置。谢谢你的任何建议:)

//
//  ContentView.swift
//  Parked
//
//  Created by Luke Jamison on 11/2/21.
//

import SwiftUI
import MapKit
import CoreLocation
import CoreLocationUI

struct ContentView: View {
    static let sharedV = ContentView()
    @StateObject private var viewModel = ContentViewModel()
    
    
    @State var addressData: String = ""
    @State private var showingImagePicker = false
    @State private var image: Image?
    @State private var inputImage: UIImage?
    //@State var geo: Any = locationManager.location
    var body: some View {
        NavigationView {
            VStack {
                Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
                    .onAppear {
                        viewModel.checkIFLocationServicesisEnabled()
                        setData()
                        //viewModel.runSetDatafunc()
                    }.frame(maxWidth: .infinity, maxHeight: .infinity).cornerRadius(20).padding()
                
                Form {
                    Section(header: Text("Add Location Details")) {
                        Text(addressData)
                        Menu {
                            Button(action: {
                                self.showingImagePicker = true
                            }) {
                                Label("Take Picture", systemImage: "camera.shutter.button")
                            }
                            image?.resizable()
                                .frame(width: 250, height: 250)
                                .clipShape(Circle())
                                .overlay(Circle().stroke(Color.white, lineWidth: 4))
                                .shadow(radius: 10)
                            
                            
                        } label: {
                            Label("Add Image", systemImage: "photo.on.rectangle.angled")
                        }
                        
                    }
                    if image != nil {
                        Section(header: Text("Image Taken")) {
                            ZStack {
                                Rectangle().fill(Color.secondary)
                                
                                if image != nil {
                                    image?.resizable().scaledToFit()
                                } else {
                                    
                                }
                            }
                            Button(action: {
                                image = nil
                            }) {
                                Label("Remove Picture", systemImage: "trash.fill")
                            }
                        }
                    } else {
                        
                    }
                }.sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
                    ImagePicker(image: self.$inputImage)
                    
                }.navigationBarTitle("Parked", displayMode: .automatic)
            }
        }.navigationViewStyle(.stack)
    }
    
    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
        
    }
    func setData() {
        ContentViewModel.shared.resolveLoactionName(with: viewModel.location) { [self] locationName in
            self.addressData = locationName
        }
    }
    
    
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
        static let shared = ContentViewModel()
        
        @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33.4, longitude: -117.4), span:
                                                    MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
        
        
        @Published var address = ""
        @Published var location = CLLocation.init(latitude: 33, longitude: -117)
        
        var locationManager: CLLocationManager?
        
        func checkIFLocationServicesisEnabled() {
            if CLLocationManager.locationServicesEnabled() {
                locationManager = CLLocationManager()
                locationManager!.delegate = self
                //locationManager?.desiredAccuracy = kCLLocationAccuracyBest
            } else {
                print("this is off turn in the settings...")
            }
        }
        
        public func resolveLoactionName(with location: CLLocation, completion: @escaping ((String) -> Void)) {
            
            let geocoder = CLGeocoder()
            geocoder.reverseGeocodeLocation(location, preferredLocale: .current) { placemarks, error in
                guard let place = placemarks?.first, error == nil else {
                    completion("")
                    return
                }
                print(place)
                var name = ""
                
                if let locality = place.subThoroughfare {
                    name += locality
                }
                
                if let street = place.thoroughfare {
                    name += " \(street)"
                }
                
                if let city = place.locality {
                    name += " \(city)"
                }
                
                if let adminRegion = place.administrativeArea {
                    name += ", \(adminRegion)"
                }
                
                if let zipCode = place.postalCode {
                    name += " \(zipCode)"
                }
                
                completion(name)
            }
        }
        
         private func checkLocationAuth() {
            guard let locationManager = locationManager else { return }
            
            switch locationManager.authorizationStatus {
                
            case .notDetermined:
                locationManager.requestWhenInUseAuthorization()
            case .restricted:
                print("location is restricted")
            case .denied:
                print("location is denied in settings for app")
            case .authorizedAlways, .authorizedWhenInUse:
                region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
                ContentView.sharedV.setData()
                self.location = CLLocation(latitude: region.self.center.latitude, longitude: region.self.center.longitude)
                print(location)
                //runSetDatafunc()
                //address = "\(locationManager.location!.coordinate.latitude), \(locationManager.location!.coordinate.longitude)"
                /*geocode(latitude: region.center.latitude, longitude: region.center.longitude) { placemark, error in
                    let placemark = placemark?.first
                    //print(placemark?.description)
                    //self.address = "\(placemark?.thoroughfare), \(placemark?.locality), \(placemark?.administrativeArea)"
                }*/
            @unknown default:
                break
            }
            
        }
        public func runSetDatafunc() {
           
        }
        func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
            checkLocationAuth()
            
        }
        
    }
    
}

标签: iosswiftxcodeswiftui

解决方案


不幸的是,由于 SwiftUI 视图的瞬态特性,你肯定不会有幸从View. 但是,您不需要它,因为您已经发布了regionand location,您View将根据它进行更新。

这是您的代码的简化/重构版本:


struct ContentView: View {
    @StateObject private var viewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Text(viewModel.address)
            Map(coordinateRegion: $viewModel.region, showsUserLocation: true)
                .onAppear {
                    viewModel.checkIfLocationServicesisEnabled()
                }
        }
    }
}

final class ContentViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
    static let shared = ContentViewModel()
    
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 33.4, longitude: -117.4), span:
                                                MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
    @Published var address = ""
    @Published var location = CLLocation.init(latitude: 33, longitude: -117)
    
    private var lastResolved : CLLocation = .init()
    
    var locationManager: CLLocationManager?
    
    func checkIfLocationServicesisEnabled() {
        if CLLocationManager.locationServicesEnabled() {
            locationManager = CLLocationManager()
            locationManager?.startUpdatingLocation()
            locationManager?.delegate = self
            //locationManager?.desiredAccuracy = kCLLocationAccuracyBest
        } else {
            print("this is off turn in the settings...")
        }
    }
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let last = locations.last else { return }
        
        if last.distance(from: lastResolved) > 200 {
            resolveLocationName(with: last) { address in
                self.address = address
                self.lastResolved = last
            }
        }
        
        self.location = last
    }
    
    public func resolveLocationName(with location: CLLocation, completion: @escaping ((String) -> Void)) {
        
        let geocoder = CLGeocoder()
        geocoder.reverseGeocodeLocation(location, preferredLocale: .current) { placemarks, error in
            guard let place = placemarks?.first, error == nil else {
                completion("")
                return
            }
            print(place)
            var name = ""
            
            if let locality = place.subThoroughfare {
                name += locality
            }
            
            if let street = place.thoroughfare {
                name += " \(street)"
            }
            
            if let city = place.locality {
                name += " \(city)"
            }
            
            if let adminRegion = place.administrativeArea {
                name += ", \(adminRegion)"
            }
            
            if let zipCode = place.postalCode {
                name += " \(zipCode)"
            }
            
            completion(name)
        }
    }
    
    private func checkLocationAuth() {
        guard let locationManager = locationManager else { return }
        
        switch locationManager.authorizationStatus {
            
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        case .restricted:
            print("location is restricted")
        case .denied:
            print("location is denied in settings for app")
        case .authorizedAlways, .authorizedWhenInUse:
            region = MKCoordinateRegion(center: locationManager.location!.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
            self.location = CLLocation(latitude: region.self.center.latitude, longitude: region.self.center.longitude)
            print(location)
        @unknown default:
            break
        }
    }
    
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        checkLocationAuth()
    }
}

请注意,我正在调用startUpdatingLocation()和实施didUpdateLocations——这些将允许您获取/更新用户的位置。

在模拟器中,您可以使用 Features->Location 菜单来模拟不同的地点。您会看到它解析不同的位置并在屏幕顶​​部显示地址。


推荐阅读