Swift: геозона / геолокация рядом с местоположением пользователя

Проблема:

Я пытаюсь сделать так, чтобы он все время использовал местоположение пользователя и постоянно проверял, находится ли он в пределах 5 миль от CLLocationCoordinate2D точек, которые я установил. Если это так, он отправляет предупреждение, если приложение открыто, или уведомление, если приложение закрыто.

Полезная информация:

В моем проекте у меня есть 4 быстрых файла:

Locations.swift набрал CLLocationCoordinate2D балла.

Utilities.swift содержит простое оповещение.

UserLocation.swift извлекает и обновляет местоположение пользователя.

GeoLocationViewController.swift отслеживает местоположения

Примечание.

Некоторый код может быть не в том месте или неактуален, я просматривал другой проект, пытаясь извлечь из него соответствующий код для использования в моем проекте. Пожалуйста, скажите мне, если вы заметили ошибку.

Код:

Locations.swift:

import UIKit
import MapKit

class Locations: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        var radius: CLLocationDistance = 5        
        let arroyo = CLLocationCoordinate2D (latitude: 33.781327997137595, longitude: -116.46394436519012)
        var arroyoCoord: CLLocationCoordinate2D = arroyo

        let buddyrogers = CLLocationCoordinate2D (latitude: 33.78051204742721, longitude: -116.46362250010833)
        var buddyCoord: CLLocationCoordinate2D = buddyrogers

        let chopsticks = CLLocationCoordinate2D (latitude: 33.815995425565184, longitude: -116.44107442645873)
        let colorfulfountain = CLLocationCoordinate2D (latitude: 33.80443304398751, longitude: -116.45723923544313)
        let diamond = CLLocationCoordinate2D (latitude: 33.80216859530781, longitude: -116.45711048941041)
        let dinahshore = CLLocationCoordinate2D (latitude: 33.806554795852996, longitude: -116.47734507421876)
        let fountoflife = CLLocationCoordinate2D (latitude: 33.78075282028137, longitude: -116.46407847564086)
        let fountains = CLLocationCoordinate2D (latitude: 33.780141969313235, longitude: -116.46346156756744)
        let historicphoto = CLLocationCoordinate2D (latitude: 33.78130570353292, longitude: -116.46389072100982)
        let holistic = CLLocationCoordinate2D (latitude: 33.781338029257775, longitude: -116.46408249895438)
        let hollywoodheroes = CLLocationCoordinate2D (latitude: 33.78095792254918, longitude: -116.45820483068849)
        let indiangathering = CLLocationCoordinate2D (latitude: 33.78136366689296, longitude: -116.46371905963287)
        let indianwomen = CLLocationCoordinate2D (latitude: 33.78622660767695, longitude: -116.45820483068849)
        let cathedrals = CLLocationCoordinate2D (latitude: 33.844502990031124, longitude: -116.45834321534426)
        let firehouse = CLLocationCoordinate2D (latitude: 33.78103817982461, longitude: -116.46700744788512)
        let perfectunion = CLLocationCoordinate2D (latitude: 33.778193459376865, longitude: -116.45877843062743)
        let lizards = CLLocationCoordinate2D (latitude: 33.78104263855992, longitude: -116.46340792338714)
        let cactus = CLLocationCoordinate2D (latitude: 33.782598723009976, longitude: -116.46699671904906)
        let swisscheese = CLLocationCoordinate2D (latitude: 33.78121541437478, longitude: -116.46472086469993)
        let newbeginning = CLLocationCoordinate2D (latitude: 33.78049421237406, longitude: -116.46463101069793)
        let thunderbolt = CLLocationCoordinate2D (latitude: 33.80140187863324, longitude: -116.46646603445436)
        let tictoc = CLLocationCoordinate2D (latitude: 33.80156235478469, longitude: -116.45524367193605)
        let wheeloftime = CLLocationCoordinate2D (latitude: 33.815987530910135, longitude: -116.45892863433227)         
        let artevita = CLLocationCoordinate2D (latitude: 33.7826633, longitude: -116.46041969999999)
        let coachellaart = CLLocationCoordinate2D (latitude: 33.78012700000001, longitude: -116.46571840000001)
        let colinfisher = CLLocationCoordinate2D (latitude: 33.7819228, longitude: -116.46002010000001)
        let garycreative = CLLocationCoordinate2D (latitude: 33.782660, longitude: -116.462141)
        let lesliejean = CLLocationCoordinate2D (latitude: 33.78404799999999, longitude: -116.4635222)
        let rebeccafine = CLLocationCoordinate2D (latitude: 33.782487, longitude: -116.460564)        
        let agnes = CLLocationCoordinate2D (latitude: 33.77571242620008, longitude: -116.46372063254091)
       let willardprice = CLLocationCoordinate2D (latitude: 33.77489419346815, longitude: -116.46667910908434)
       let adobe = CLLocationCoordinate2D (latitude: 33.77479870632753, longitude: -116.46673050629039)
        let valsamuelson = CLLocationCoordinate2D (latitude: 33.76802162366799, longitude: -116.46920998147584)
        let gallito = CLLocationCoordinate2D (latitude: 33.7794358, longitude: -116.4612692)        
        let townsquare = CLLocationCoordinate2D (latitude: 33.7810365, longitude: -116.46464559999998)
        let ocotillo = CLLocationCoordinate2D (latitude: 33.805963, longitude: -116.46349980000002)
        let century = CLLocationCoordinate2D (latitude: 33.8269913, longitude: -116.4424588)
        let denniskeat = CLLocationCoordinate2D (latitude: 33.8304982, longitude: -116.45744730000001)
        let memorial = CLLocationCoordinate2D (latitude: 33.78318512716751, longitude: -116.46681405767208)
        let patriot = CLLocationCoordinate2D (latitude: 33.8019902897174, longitude: -116.44000872473146)
        let panorama = CLLocationCoordinate2D (latitude: 33.83861734636407, longitude: -116.46799619895023)
        let secondst = CLLocationCoordinate2D (latitude: 33.78069442561766, longitude: -116.45910418200071)
        let dogpark = CLLocationCoordinate2D (latitude: 33.7804269, longitude: -116.46041309999998)


    }

}

Утилиты.swift:

import UIKit
import MapKit

func showSimpleAlertWithTitle(title: String!, message: String, viewController: UIViewController) {
    let alert = UIAlertController(title: title, message: message, preferredStyle: .Alert)
    let action = UIAlertAction(title: "OK", style: .Cancel, handler: nil)
    alert.addAction(action)
    viewController.presentViewController(alert, animated: true, completion: nil)
}

UserLocation.swift:

import UIKit
import CoreLocation

class UserLocation: UIViewController, CLLocationManagerDelegate {
    var locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        if (CLLocationManager.locationServicesEnabled()) {
            locationManager = CLLocationManager()
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.requestAlwaysAuthorization()
            locationManager.startUpdatingLocation()
        }

        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()

    }

}

GeoLocationViewController.swift:

import UIKit
import CoreLocation

class GeoLocationViewController: UIViewController, CLLocationManagerDelegate {

    let locationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        locationManager.delegate = self
        locationManager.requestAlwaysAuthorization()

        // Do any additional setup after loading the view.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func regionWithGeotification(geotification: Locations) -> CLCircularRegion {
        // 1
        let region = CLCircularRegion(center: geotification.coordinate, radius: geotification.radius, identifier: geotification.identifier)
        // 2
        region.notifyOnEntry = (geotification.eventType == .OnEntry)
        region.notifyOnExit = !region.notifyOnEntry
        return region
    }

    func startMonitoringGeotification(geotification: Locations) {
        // 1
        if !CLLocationManager.isMonitoringAvailableForClass(CLCircularRegion) {
            showSimpleAlertWithTitle("Error", message: "Geofencing is not supported on this device!", viewController: self)
            return
        }
        // 2
        if CLLocationManager.authorizationStatus() != .AuthorizedAlways {
            showSimpleAlertWithTitle("Warning", message: "Your geotification is saved but will only be activated once you grant permission to access the device location.", viewController: self)
        }
        // 3
        let region = regionWithGeotification(geotification)
        // 4
        locationManager.startMonitoringForRegion(region)
    }

    func stopMonitoringGeotification(geotification: Locations) {
        for region in locationManager.monitoredRegions {
            if let circularRegion = region as? CLCircularRegion {
                if circularRegion.identifier ==  geotification.identifier {
                    locationManager.stopMonitoringForRegion(circularRegion)
                }
            }
        }
    }

    func locationManager(manager: CLLocationManager, monitoringDidFailForRegion region: CLRegion?, withError error: NSError) {
        print("Monitoring failed for region with identifier: \(region!.identifier)")
    }

    func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
        print("Location Manager failed with the following error: \(error)")
    }

}

person Hunter    schedule 30.01.2016    source источник
comment
Что именно вы хотите? Где ты застрял?   -  person Vishnu gondlekar    schedule 30.01.2016
comment
@Vishnugondlekar Я хочу, чтобы приложение всегда использовало местоположение пользователя и постоянно проверяло, находятся ли они в пределах 5 миль от местоположений, которые я указал в Locations.swift. Я просто не уверен, что у меня есть правильный код для этого. Я также получал код из другого приложения. У меня есть ошибки в GeoLocationViewController.swift на let region = CLCircularRegion(center: geotification.coordinate, radius: geotification.radius, identifier: geotification.identifier) Я получаю, что «Местоположения» не имеют «координат» члена. как и на этой строке тоже: if circularRegion.identifier == geotification.identifier.   -  person Hunter    schedule 30.01.2016
comment
какой ViewController открывает ваше приложение? Я предполагаю, что это GeotificationsViewController?   -  person olympia    schedule 22.02.2016
comment
@mmarkman не открывается для контроллера представления. Я хотел бы, чтобы он просто работал в фоновом режиме.   -  person Hunter    schedule 22.02.2016


Ответы (3)


Прежде всего, измените обратно startMonitoringGeotification(), regionWithGeotification() и stopMonitoringGeotification(), чтобы принять Geotification, как в учебнике Рэя Вендерлиха. Убедитесь, что вы добавили файл Geotification.swift из его начального кода в свой проект.

Кроме того, убедитесь, что ваш Main.storyboard запускает файл ViewController. Без этого шага ни один из ваших кодов не запустится.

1) переопределить свой класс Locations более просто в Locations.swift:

import UIKit
import MapKit

class Locations {

  static let locations:[String:CLLocationCoordinate2D] = [
    "buddyrogers" : CLLocationCoordinate2D(latitude: 33.815995425565184, longitude: -116.44107442645873),
    "diamond"     : CLLocationCoordinate2D(latitude: 33.802168595307814, longitude: -116.45711048941041),
     . 
     .    // add your locations
     .
    ]
  }
}

как предложил @hungry-yeti

2) Вы можете определить showSimpleAlertWithTitle() в своем классе GeotificationViewController. Попробуйте вызвать его в своем ViewDidLoad(), чтобы проверить. Теперь вы можете удалить файл Utilities.swift.

3) Я думаю, что вы можете игнорировать/удалить UserLocation.swift, это кажется ненужным

4) Поместите этот код внутри GeotificationViewController ViewDidLoad:

let radius = CLLocationDistance(8046.72) // 5 miles in meters

for location in Locations.locations {
  let g = Geotification(coordinate: location.1, radius: radius, identifier: location.0, note: "test", eventType: EventType.OnEntry)
    startMonitoringGeotification(g)
}

5) Я надеюсь, что это поможет и упростит ваш код. Вечеринка, ответьте здесь, если у вас есть какие-либо вопросы.

person olympia    schedule 22.02.2016
comment
У меня есть несколько ошибок по классам. У вас есть дропбокс? Если да, то какая у него электронная почта? Если вы не возражаете, я мог бы сделать пример проекта и отправить его вам только с этими файлами, чтобы вы могли видеть ошибки, а не я объяснял их все здесь. Как только они будут исправлены, вы можете просто отправить его обратно. Я нахожу это простым способом решения проблем, так как он работал в прошлом. - person Hunter; 23.02.2016
comment
Можете ли вы опубликовать свой проект на Github, чтобы другие могли извлечь из него уроки? Если нет, то я сделаю Dropbox - person olympia; 23.02.2016
comment
У меня нет Github, и я не знаю, как его использовать. Если бы мы могли использовать Dropbox сейчас, это было бы здорово, а потом я разберусь с Github. Как только мы решим проблемы, я опубликую ссылку на Github. - person Hunter; 23.02.2016

Похоже, вы используете учебник Рэя Вендерлиха. Это хорошо, я тоже нашел это очень полезным.

Во-первых, единицей измерения CLLocationDistance являются метры, поэтому код, который у вас есть, указывает радиус 5 метров, который не будет таким полезным, как вы можете надеяться; значение 8046,72 ближе к 5 милям.

Что касается конкретной ошибки, Locations — это класс, в который вы вставили все значения CLLocationCoordinate2D, и, конечно же, он не имеет члена с именем координата. Если вы используете учебник, о котором я думаю, вам нужно будет загрузить эти координаты в экземпляры класса Geotification.

Вот непроверенный код:

  // Load the various coords into an array:
  var locations:[(note:String, coords:CLLocationCoordinate2D)] = []
  locations +=[(note: "arroyo", CLLocationCoordinate2D( latitude: 33.781327997137595, longitude: -116.46394436519012)]
  locations +=[(note: "buddyrogers", CLLocationCoordinate2D( latitude: 33.78051204742721, longitude: -116.46362250010833)]
  // ...

  let radius = 8000  // ~5 miles rounded to nearest km
  // Load the locations into geotifications:
  for location in locations {
     let geotification = Geotification(coordinate: location.cords, radius: radius, identifier: NSUUID().UUIDString, note: location.note, eventType: EventType.OnEnter)
     startMonitoringGeotification(geotification)
  }

Теперь имейте в виду, что существует жесткое ограничение в 20 отслеживаемых регионов для каждого приложения, поэтому, если у вас их больше, вам нужно будет динамически определить ближайшие 20 регионов, а затем отслеживать эти регионы.

person hungri-yeti    schedule 30.01.2016
comment
Как мне загрузить координаты в экземпляры? - person Hunter; 31.01.2016

введите здесь описание изображенияВот GeoFenceНесколько геозон с AppleMap в Xcode 12.3 (Swift 5) 100% работает

import UIKit
import MapKit
import CoreLocation

struct GeotificationData {
   var lat : String?
   var long : String?
}

class AppleMapVC: UIViewController {

   @IBOutlet weak var mapView: MKMapView!

   lazy var locationManager = CLLocationManager()
   var arrGeoFenceData = [GeotificationData]()


   //MARK:- VIEW CONTROLLER LIFE CYCLE METHOD

   override func viewDidLoad() {
       super.viewDidLoad()
       title = "GeoFence"
       UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in }
       if CLLocationManager.locationServicesEnabled() {
           locationManager.delegate = self
           locationManager.requestAlwaysAuthorization()
           locationManager.desiredAccuracy = kCLLocationAccuracyBest //optimize power performanc Battery
           locationManager.startUpdatingLocation()
           arrGeoFenceData = [GeotificationData(lat: "21.7469", long: "74.1240"),
                           GeotificationData(lat: "21.1702", long: "72.8311"),
                           GeotificationData(lat: "19.9975", long: "73.7898"),
                           GeotificationData(lat: "20.1738", long: "72.7640"),
                           GeotificationData(lat: "19.0760", long: "72.8777"),
                           GeotificationData(lat: "18.5204", long: "73.8567")]
       }
       getGeoFencing()
    
   }

   //show notification

   func showNotification(title:String, message:String) {
       let content = UNMutableNotificationContent()
       content.title = title
       content.body = message
       content.badge = 1
       content.sound = .default
       let request = UNNotificationRequest(identifier: "notifi", content: content, trigger: nil)
       UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
   }
   func monitorRegionAtLocation(center: CLLocationCoordinate2D, identifier: String ) {
       // Make sure the devices supports region monitoring.
       if CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
           // Register the region.
           let maxDistance = CLLocationDistance(30000)
           let region = CLCircularRegion(center: center,
             radius: maxDistance, identifier: identifier)
           region.notifyOnEntry = true
           region.notifyOnExit = false
           let circle = MKCircle(center: center, radius: maxDistance)
           mapView.addOverlay(circle)
           locationManager.startMonitoring(for: region)
        
       }
   }


   func getGeoFencing() {
       for item in arrGeoFenceData {
           print("Your location with lat and long :- \(item)")
           let cordi = CLLocationCoordinate2D(latitude: Double(item.lat!)!, longitude: Double(item.long!)!)
           monitorRegionAtLocation(center: cordi, identifier: "Geofence")
       }
   }

   //MARK:- UIBUTTON CLICKED

   @IBAction func btnRotationClicked(_ sender: Any) {
       mapView.setUserTrackingMode(.followWithHeading, animated: true)
   }

}

//MARK:- LOCATIONMANAGER DELEGATE METHOD FOR UPDATE LOCATION

extension AppleMapVC : CLLocationManagerDelegate {
   func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
       if let location = locations.first {
           locationManager.stopUpdatingLocation()
  //               locationManager.startUpdatingLocation()
           render(location)
       }
   }

   func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
       let alert = UIAlertController.init(title: "You enter in location", message: "enter in geofence", preferredStyle: .alert)
       alert.addAction(UIAlertAction(title: "Continue", style: UIAlertAction.Style.default, handler: nil))
    
       self.present(alert, animated: true, completion: nil)
       showNotification(title: "You entered in geofence", message: "Welcome")
   }
   func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
       let alert = UIAlertController.init(title: "You exit in location", message: "exit in geofence", preferredStyle: .alert)
       alert.addAction(UIAlertAction(title: "Continue", style: UIAlertAction.Style.default, handler: nil))
    
       self.present(alert, animated: true, completion: nil)
       showNotification(title: "You exit in geofence", message: "come again")
   }

   func render(_ location: CLLocation) {
       let coordinate = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
       let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
       let region = MKCoordinateRegion(center: coordinate, span: span)
       mapView.setRegion(region, animated: true)
       mapView.showsUserLocation = true
//        let pin = MKPointAnnotation()
//        pin.coordinate = coordinate
//        mapView.addAnnotation(pin)
   }
}

 //MARK:- MKMAPVIEW DELEGATE METHOD

extension AppleMapVC : MKMapViewDelegate {
   func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
       guard let circleOverlay = overlay as? MKCircle else {
        return MKOverlayRenderer()
       }
       let circleRender = MKCircleRenderer(circle: circleOverlay)
       circleRender.strokeColor = .red
       circleRender.fillColor = .red
       circleRender.alpha = 0.4
       return circleRender
   }
}
person Karan Vakharia    schedule 04.01.2021