iOS CLLocationManager in a separate class

2019-01-10 19:44发布

问题:

I have multiple view controllers that need to get the user's location, so I wanted to create a separate class that the ViewControllers can call to get the user's latest location.

locationManager:didUpdateToLocation:fromLocation returns void. How do I pass the latitude and longitude data back to my ViewControllers as soon as the user's latitude and longitude is calculated?

I could try writing getters and setters in my locationManaging class, but if I do that, how do I know when to call the latitude getter and longitude getter methods from my ViewController class? How do I hold the ViewController's main thread to wait for the latitude and longitude values from the locationManaging class?

Thanks!

回答1:

Create a singleton class which has a latitude and longitude properties, startLocating and endLocating. In the class, create a CLLocationManager instance, and set its delegate to be the singleton. In startLocating and endLocating, call the appropriate methods of the CLLocationManager instance. Make the delegate methods update the latitude and longitude properties. In other ViewControllers, read this singleton's latitude and longitude properties.

To know when to read those properties from another ViewController, set an observer on these properties (see the NSKeyValueObserving Protocol Reference

Before doing this, look up the Internets for existing code.

After doing this, upload it to GitHub with a permissive license.



回答2:

As user1071136 said, a singleton location manager is probably what you want. Create a class, a subclass of NSObject, with just one property, a CLLocationManager.

LocationManagerSingleton.h:

#import <MapKit/MapKit.h>

@interface LocationManagerSingleton : NSObject <CLLocationManagerDelegate>

@property (nonatomic, strong) CLLocationManager* locationManager;

+ (LocationManagerSingleton*)sharedSingleton;

@end

LocationManagerSingleton.m:

#import "LocationManagerSingleton.h"

@implementation LocationManagerSingleton

@synthesize locationManager;

- (id)init {
    self = [super init];

    if(self) {
        self.locationManager = [CLLocationManager new];
        [self.locationManager setDelegate:self];
        [self.locationManager setDistanceFilter:kCLDistanceFilterNone];
        [self.locationManager setHeadingFilter:kCLHeadingFilterNone];
        [self.locationManager startUpdatingLocation];
        //do any more customization to your location manager
    }

    return self;
}    

+ (LocationManagerSingleton*)sharedSingleton {
    static LocationManagerSingleton* sharedSingleton;
    if(!sharedSingleton) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedSingleton = [LocationManagerSingleton new];
        }
    }

    return sharedSingleton;
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    //handle your location updates here
}

- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading {
    //handle your heading updates here- I would suggest only handling the nth update, because they
    //come in fast and furious and it takes a lot of processing power to handle all of them
}

@end

To get the most recently received location, simply use [LocationManagerSingleton sharedSingleton].locationManager.location. It might take a few seconds to warm up the GPS to get accurate locations.



回答3:

Based on the above answers, here is what I did and you can find the complete example on github https://github.com/irfanlone/CLLocationManager-Singleton-Swift

Just import this file in your project, then you can either choose to implement the LocationUpdateProtocol or listen to notification for location updates

import MapKit

protocol LocationUpdateProtocol {
    func locationDidUpdateToLocation(location : CLLocation)
}

/// Notification on update of location. UserInfo contains CLLocation for key "location"
let kLocationDidChangeNotification = "LocationDidChangeNotification"

class UserLocationManager: NSObject, CLLocationManagerDelegate {

    static let SharedManager = UserLocationManager()

    private var locationManager = CLLocationManager()

    var currentLocation : CLLocation?

    var delegate : LocationUpdateProtocol!

    private override init () {
        super.init()
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
        locationManager.requestAlwaysAuthorization()
        self.locationManager.startUpdatingLocation()
    }

    // MARK: - CLLocationManagerDelegate

    func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
        currentLocation = newLocation
        let userInfo : NSDictionary = ["location" : currentLocation!]

        dispatch_async(dispatch_get_main_queue()) { () -> Void in
            self.delegate.locationDidUpdateToLocation(self.currentLocation!)
            NSNotificationCenter.defaultCenter().postNotificationName(kLocationDidChangeNotification, object: self, userInfo: userInfo as [NSObject : AnyObject])
        }
    }

}

Usage:

class ViewController: UIViewController, LocationUpdateProtocol {

    var currentLocation : CLLocation!

    override func viewDidLoad() {
        super.viewDidLoad()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "locationUpdateNotification:", name: kLocationDidChangeNotification, object: nil)

        let LocationMgr = UserLocationManager.SharedManager
        LocationMgr.delegate = self

    }

    // MARK: - Notifications

    func locationUpdateNotification(notification: NSNotification) {
        let userinfo = notification.userInfo
        self.currentLocation = userinfo!["location"] as! CLLocation
        print("Latitude : \(self.currentLocation.coordinate.latitude)")
        print("Longitude : \(self.currentLocation.coordinate.longitude)")

    }

    // MARK: - LocationUpdateProtocol

    func locationDidUpdateToLocation(location: CLLocation) {
        currentLocation = location
        print("Latitude : \(self.currentLocation.coordinate.latitude)")
        print("Longitude : \(self.currentLocation.coordinate.longitude)")
    }

}


回答4:

Here's what I did when implementing a location manger singleton in swift. It's based on user1071136's strategy, as well as this swift pattern.

//
//  UserLocationManager.swift
//
//  Use: call SharedUserLocation.currentLocation2d from any class


import MapKit

class UserLocation: NSObject, CLLocationManagerDelegate {

    var locationManager = CLLocationManager()

    // You can access the lat and long by calling:
    // currentLocation2d.latitude, etc

    var currentLocation2d:CLLocationCoordinate2D?


    class var manager: UserLocation {
        return SharedUserLocation
    }

    init () {
        super.init()
        if self.locationManager.respondsToSelector(Selector("requestAlwaysAuthorization")) {
            self.locationManager.requestWhenInUseAuthorization()
        }
        self.locationManager.delegate = self
        self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
        self.locationManager.distanceFilter = 50
        self.locationManager.startUpdatingLocation()
    }

    func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
        self.currentLocation2d = manager.location.coordinate

    }
}

let SharedUserLocation = UserLocation()


回答5:

In the process of learning to obtain user location in a singleton class, I downloaded this code https://github.com/irfanlone/CLLocationManager-Singleton-Swift. After foddleing with it to make in run on Xcode8.3.3 Swift 3, I cannot get any output in the debug console. In fact I can't even get this location autherization dlalog to display. I suspect the data from the singleton is not passed to the view controller, but I cannot fine the solution, can you see what is wrong? Thanks.

The corrected code for Swift 3 is:

//The singleton:
import MapKit

protocol LocationUpdateProtocol {
func locationDidUpdateToLocation(_ location : CLLocation)
}

let kLocationDidChangeNotification = "LocationDidChangeNotification"

class UserLocationManager: NSObject, CLLocationManagerDelegate {

static let SharedManager = UserLocationManager()

fileprivate var locationManager = CLLocationManager()

var currentLocation : CLLocation?

var delegate : LocationUpdateProtocol!

fileprivate override init () {
    super.init()
    self.locationManager.delegate = self
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
    self.locationManager.distanceFilter = kCLLocationAccuracyHundredMeters
    locationManager.requestAlwaysAuthorization()
    self.locationManager.startUpdatingLocation()
}

// MARK: - CLLocationManagerDelegate
func locationManager(manager: CLLocationManager,didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
    currentLocation = newLocation
    let userInfo : NSDictionary = ["location" : currentLocation!]


    DispatchQueue.main.async() { () -> Void in
        self.delegate.locationDidUpdateToLocation(self.currentLocation!)
        NotificationCenter.default.post(name: Notification.Name(kLocationDidChangeNotification), object: self, userInfo: userInfo as [NSObject : AnyObject])
    }
}

}



  // The ViewController
    import UIKit
    import CoreLocation

 class ViewController: UIViewController, LocationUpdateProtocol {

var currentLocation : CLLocation!

override func viewDidLoad() {
    super.viewDidLoad()

    NotificationCenter.default.addObserver(self, selector: #selector(ViewController.locationUpdateNotification(_:)), name: NSNotification.Name(rawValue: kLocationDidChangeNotification), object: nil)

    let LocationMgr = UserLocationManager.SharedManager
    LocationMgr.delegate = self

}

// MARK: - Notifications

func locationUpdateNotification(_ notification: Notification) {
    let userinfo = notification.userInfo
    self.currentLocation = userinfo!["location"] as! CLLocation
    print("Latitude : \(self.currentLocation.coordinate.latitude)")
    print("Longitude : \(self.currentLocation.coordinate.longitude)")

}

// MARK: - LocationUpdateProtocol

func locationDidUpdateToLocation(_ location: CLLocation) {
    currentLocation = location
    print("Latitude : \(self.currentLocation.coordinate.latitude)")
    print("Longitude : \(self.currentLocation.coordinate.longitude)")
}


}