[Pins latitude]: unrecognized selector sent to instance - ios

I'm having trouble with CoreData a little bit, everything work fine separately but when try to run them everything togather a got the error [Pins latitude]: unrecognized selector sent to instance
When I fetch the request in viewDidLoad I can't add any pen to the map .. but if I load the store with out fetching the request I can add pins to the map and they would get stored.
here is my TravalLocationsMap.swift
class TravelLocationsMap : UIViewController, NSFetchedResultsControllerDelegate{
#IBOutlet var PinchGesture: UIPinchGestureRecognizer!
#IBOutlet weak var MapView: MKMapView!
let userDefaults = UserDefaults()
var keysValues = [String : CLLocationDegrees]()
let locationManger = CLLocationManager()
var pinCoordinate = CGPoint()
var operationQueue: [BlockOperation]!
var fetchedResultsController:NSFetchedResultsController<Pins>!
fileprivate func setupFetchedResultsController() {
let fetchRequest:NSFetchRequest<Pins> = Pins.fetchRequest()
let sortDescriptor = NSSortDescriptor(key: "creationDate", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: AppDelegate.dataController.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultsController.delegate = self
do {
try fetchedResultsController.performFetch()
} catch {
fatalError("The fetch could not be performed: \(error.localizedDescription)")
}
}
#IBAction func PinchGesture(_ sender: Any) {
guard let guestureView = PinchGesture.view else {
return
}
guestureView.transform = guestureView.transform.scaledBy(x: PinchGesture.scale, y: PinchGesture.scale)
PinchGesture.scale = 1
}
fileprivate func restoreUserDefaultsData() {
switch (userDefaults.value(forKey: "lat") as? CLLocationDegrees,userDefaults.value(forKey: "long") as? CLLocationDegrees , userDefaults.value(forKey: "latDelta") as? CLLocationDegrees , userDefaults.value(forKey: "longDelta") as? CLLocationDegrees) {
case let (latitude?, longitude?,latDelta?,longDelta?):
let location = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let regionCoordinate = setRegion(location: location, latDelta: latDelta, longDelta: longDelta)
MapView.setRegion(regionCoordinate, animated: true)
break
default:
print("Something Went Wrong Brother!")
break
}
}
fileprivate func populatePinsOnTheMap(){
guard let pins = fetchedResultsController.fetchedObjects else {
return
}
for pin in pins {
let Annotation = MKPointAnnotation()
Annotation.coordinate = CLLocationCoordinate2D(latitude: pin.latitude, longitude: pin.longitude)
self.MapView.addAnnotation(Annotation)
}
}
override func viewDidLoad() {
super.viewDidLoad()
MapView.delegate = self
setupFetchedResultsController()
restoreUserDefaultsData()
populatePinsOnTheMap()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// fetchedResultsController = nil
}
func setupRecognizers() {
let longTapRecognizer = UILongPressGestureRecognizer(target: self, action:#selector(handleLongPress(recognizer:)))
view.addGestureRecognizer(longTapRecognizer)
}
#objc func handleLongPress(recognizer: UIGestureRecognizer) {
if recognizer.state == .began {
pinCoordinate = recognizer.location(in: MapView)
let coordinate = MapView.convert(pinCoordinate, toCoordinateFrom: MapView)
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
self.MapView.addAnnotation(annotation)
addPins(latitude: coordinate.latitude, longitude: coordinate.longitude)
recognizer.state = .ended
}
}
func addPins(latitude:CLLocationDegrees,longitude:CLLocationDegrees){
let pin = Pins(context: AppDelegate.dataController.viewContext)
print("This is Pin instance \(pin)")
pin.latitude = latitude
pin.longitude = longitude
pin.creationDate = Date()
print("This is pin after set the values \(pin)")
try? AppDelegate.dataController.viewContext.save()
}
func setRegion(location : CLLocationCoordinate2D, latDelta:CLLocationDegrees,longDelta:CLLocationDegrees) -> MKCoordinateRegion{
let north = CLLocation(latitude: location.latitude - latDelta * 0.5, longitude: location.longitude)
let south = CLLocation(latitude: location.latitude + latDelta * 0.5, longitude: location.longitude)
let east = CLLocation(latitude: location.latitude, longitude: location.longitude - longDelta * 0.5)
let west = CLLocation(latitude: location.latitude, longitude: location.longitude + longDelta * 0.5)
let northToSouth = north.distance(from: south)
let eastToWest = east.distance(from: west)
return MKCoordinateRegion(center: location, latitudinalMeters: northToSouth, longitudinalMeters: eastToWest)
}
}
extension TravelLocationsMap : MKMapViewDelegate{
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
keysValues = ["long" : MapView.centerCoordinate.longitude , "lat" : MapView.centerCoordinate.latitude , "latDelta" : MapView.region.span.latitudeDelta , "longDelta" : MapView.region.span.longitudeDelta]
userDefaults.setValuesForKeys(keysValues)
setupRecognizers()
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.pinTintColor = .red
pinView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
else {
pinView!.annotation = annotation
}
return pinView
}
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
operationQueue = [BlockOperation]()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
let operation = BlockOperation(block: { () -> Void in
let pin = Pins()
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: pin.latitude, longitude: pin.longitude)
self.MapView.addAnnotation(annotation)})
operationQueue.append(operation)
default:
break
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
for operation in operationQueue {
operation.start()
}
operationQueue = nil
}
}
Here is my AppDelegate.swift
import UIKit
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
static var dataController = DataController(modelName: "TravalLocationMap")
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppDelegate.dataController.load()
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
Any suggestion why this is happing.
Thank you in advance.

Related

MKLocalSearch (search.start)always goes in the error flow. (SWIFT)

I've been trying to learn swift for the past few days and just started my own project! As part of the project I wanted to show nearby cocktail bars on a map, I was able to find some nice info online and have been able to show a map with my current location. I also found info on how to find nearby locations: https://developer.apple.com/documentation/mapkit/mklocalsearch/request
Unfortunately this last one never seems to work, it just goes out of the function and does not return any locations, could anyone help me further? Below is the code of my viewcontroller with the function getNearbyLandmarks which doesn't work as intended.
class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
#IBOutlet var mapView: MKMapView!
let manager = CLLocationManager()
private var landmarks: [Landmark] = [Landmark]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.delegate = self
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
manager.stopUpdatingLocation()
render(location)
}
}
func render(_ location: CLLocation){
let coordinate = CLLocationCoordinate2D(latitude: location.coordinate.latitude,
longitude: location.coordinate.longitude)
let span = MKCoordinateSpan(latitudeDelta: 0.01,
longitudeDelta: 0.01)
let region = MKCoordinateRegion(center: coordinate,
span: span)
mapView.delegate = self
mapView.setRegion(region,
animated: true)
let pin = MKPointAnnotation()
pin.coordinate = coordinate
pin.title = "Current location"
mapView.addAnnotation(pin)
self.getNearByLandmarks()
updateAnnotations(from: mapView)
}
func mapView(_ mapViewIcon: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapViewIcon.dequeueReusableAnnotationView(withIdentifier: "custom")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation,
reuseIdentifier: "custom")
annotationView?.canShowCallout = true
}else{
annotationView?.annotation = annotation
}
annotationView?.image = UIImage(named: "User")
return annotationView
}
private func getNearByLandmarks(){
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = "coffee"
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start{(response, error) in
if let response = response {
let mapItems = response.mapItems
self.landmarks = mapItems.map{
Landmark(placemark: $0.placemark)
}
}
}
}
private func updateAnnotations(from mapView: MKMapView){
let annotations = self.landmarks.map(LandmarkAnnotation.init)
mapView.addAnnotations(annotations)
}
}
You call getNearByLandmarks, and then immediately try to update the annotations:
self.getNearByLandmarks()
updateAnnotations(from: mapView)
but getNearByLandmarks is asynchronous, and takes some time to complete. You need to update the annotations whenever private var landmarks changes, one way is to update it in all the places that set that, like where you say
self.landmarks = mapItems.map{
Landmark(placemark: $0.placemark)
}
note you don't call updateAnnotations(from: mapView) there.
Or you could add a didSet property observer to landmarks itself so that updateAnnotations is called whenever it changes.

How to display customer callout screens on a map using Swift and Xcode

I am fairly new to IOS development, and I am working on creating a map to display different venues from a JSON file which works like a charm. Now, what I want to do is, once a user clicks a pin, I want a callout view to be displayed at the bottom of the map that shows the Venue's logo on the left, and the following on the right:
a phone number that's clickable so call them directly, a website url that's clickable to access the website, and finally, a directions button to open the map to show directions to the Venue.
Once a user clicks a different pin, then the information about that new venue is displayed.
Here is the portion of the code that I think needs edits, and below is the full code
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if let annotation = annotation as? Venue {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
}
return view
}
return nil
}
Please help me with step by step instructions. I am having a hard time getting around Swift.
THANKS A BUNCH!
import UIKit
import CoreLocation
import MapKit
//import SwiftyJSON
struct Place: Codable {
let id: Int
let name, address, number, imageURL: String
let lat, long: Double
enum CodingKeys: String, CodingKey {
case id, name, address, number
case imageURL = "imageUrl"
case lat, long
}
}
class YogaViewController: UIViewController {
// MARK: - Properties
var locationManager: CLLocationManager!
var mapView: MKMapView!
let centerMapButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(#imageLiteral(resourceName: "location-arrow-flat").withRenderingMode(.alwaysOriginal), for: .normal)
button.addTarget(self, action: #selector(handleCenterLocation), for: .touchUpInside)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
// MARK: - Init
override func viewDidLoad() {
super.viewDidLoad()
configureLocationManager()
configureMapView()
enableLocationServices()
// mapView.addAnnotations(venues)
// //JSON STUFF
let jsonUrlString = "https://www.elev8dfw.com/Gyms.json"
// let jsonUrlString = "https://www.elev8dfw.com/Venues.json"
// let jsonUrlString = "https://api.letsbuildthatapp.com/jsondecodable/course"
guard let url = URL(string: jsonUrlString) else {return}
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let places = try JSONDecoder().decode([Place].self, from: data)
for place in places {
// print(place.lat)
// print(place.long)
let sampleStarbucks = Venue(title: place.name, locationName: place.address, coordinate: CLLocationCoordinate2D(latitude: place.lat, longitude: place.long))
self.mapView.addAnnotation(sampleStarbucks)
}
} catch let jsonErr{
print("Error serializing json: ", jsonErr)
}
}.resume()
mapView.delegate = self
}
// MARK: - Selectors
#objc func handleCenterLocation() {
centerMapOnUserLocation()
centerMapButton.alpha = 0
}
// MARK: - Helper Functions
func configureLocationManager() {
locationManager = CLLocationManager()
locationManager.delegate = self
}
func configureMapView() {
mapView = MKMapView()
mapView.showsUserLocation = true
mapView.delegate = self
mapView.userTrackingMode = .follow
view.addSubview(mapView)
mapView.frame = view.frame
view.addSubview(centerMapButton)
centerMapButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100).isActive = true
centerMapButton.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
centerMapButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
centerMapButton.widthAnchor.constraint(equalToConstant: 50).isActive = true
centerMapButton.layer.cornerRadius = 50 / 2
centerMapButton.alpha = 0
}
func centerMapOnUserLocation() {
guard let coordinate = locationManager.location?.coordinate else { return }
let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 4000, longitudinalMeters: 4000)
mapView.setRegion(region, animated: true)
}
}
// MARK: - MKMapViewDelegate
extension YogaViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
UIView.animate(withDuration: 0.5) {
self.centerMapButton.alpha = 1
}
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if let annotation = annotation as? Venue {
let identifier = "pin"
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) as UIView
}
return view
}
return nil
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let location = view.annotation as! Venue
let launchOptions = [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
}
// MARK: - CLLocationManagerDelegate
extension YogaViewController: CLLocationManagerDelegate {
func enableLocationServices() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined:
print("Location auth status is NOT DETERMINED")
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
centerMapOnUserLocation()
case .restricted:
print("Location auth status is RESTRICTED")
case .denied:
print("Location auth status is DENIED")
case .authorizedAlways:
print("Location auth status is AUTHORIZED ALWAYS")
case .authorizedWhenInUse:
print("Location auth status is AUTHORIZED WHEN IN USE")
locationManager.startUpdatingLocation()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
centerMapOnUserLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
self.mapView.showsUserLocation = true
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
guard locationManager.location != nil else { return }
centerMapOnUserLocation()
}
}
If you are attempting to open a custom UIView coming up from the bottom upon tapping a annotation you could utilize didSelect and do something like the following:
final func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let location = view.annotation as! Venue
// Update your new custom view with the Venue here.
// Now lets show the view
self.bottomViewConstraint.constant = 0
UIView.animate(withDuration: 0.2) {
self.view.layoutIfNeeded()
}
}
So in your storyboard. Create a UIView, set its height and width constraints. Set its bottom constraint to be a negative number so it is not showing on the screen. Create an IBOutlet to the bottom constraint (I named it bottomViewConstraint in the example code). This is a very basic example, untested, to get you started.

Background Fetch is not Working on Real iOS Device?

I have created an iOS application which just sends the Location Coordinates to the server after the user's login. I have also implemented the widget which takes the snapshot of the location displayed it on the widget screen and send it to the server. The application is simple. I just want to open the app or widget for a short time in the background after 20-30 mins and send the location to the server.
For this, I have implemented the Background Fetch in the app. It is working while simulating from the debugger. But won't work on the real iOS device. App Code is below.
If there is any other solution to perform such task let me know except the Push/Remote Notifications. Thanks
AppDelegate.swift
import UIKit
import UserNotifications
import Alamofire
import CoreLocation
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {
var window: UIWindow?
var myLat:Double = 0.0
var myLong:Double = 0.0
let locationManager = CLLocationManager()
var MSISDN = ""
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
UIApplication.shared.setMinimumBackgroundFetchInterval(
UIApplication.backgroundFetchIntervalMinimum)
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self as? CLLocationManagerDelegate
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
return true
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
myLat = locValue.latitude
myLong = locValue.longitude
print(locValue)
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if let userDefaults = UserDefaults(suiteName: "group.com.demo.spoton") {
var value1 = userDefaults.bool(forKey: "IsLogin")
if value1 == true {
print (value1)
MSISDN = userDefaults.string(forKey: "MSISDN")!
var fetchResult: UIBackgroundFetchResult!
print("i'm executing a task")
let Type = 2
let root = "https://demourl.com/locreciver.php?latitude="
let url = "\(root)\(myLat)&longitude=\(myLong)&type=\(2)&msisdn=\(MSISDN)&battery=\(1)";
Alamofire.request(url, method:.post).responseString {
response in
switch response.result {
case .success:
print(response)
completionHandler(.newData)
case .failure(let error):
print(error)
completionHandler(.failed)
}
}
return
}
else{
print("notLogin")
}
}
}// fecth function
func applicationWillResignActive(_ application: UIApplication) {
}
func applicationDidEnterBackground(_ application: UIApplication) {
}
func applicationWillEnterForeground(_ application: UIApplication) {
}
func applicationDidBecomeActive(_ application: UIApplication) {
}
func applicationWillTerminate(_ application: UIApplication) {
}
}
// TodayViewController.swift //Widget
import UIKit
import NotificationCenter
import CoreLocation
import Alamofire
import Swift
import GoogleMaps
import MapKit
class TodayViewController: UIViewController, NCWidgetProviding, CLLocationManagerDelegate,MKMapViewDelegate {
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var mapImage: UIImageView!
let locationManager = CLLocationManager()
var myLat:Double = 0.0
var myLong:Double = 0.0
var MSISDN = ""
#objc func doLaunchApp(){
if let url = NSURL(string: "mainAppUrl://"){
self.extensionContext?.open(url as URL, completionHandler: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// self.extensionContext?.widgetLargestAvailableDisplayMode = NCWidgetDisplayMode.expanded
// let url = URL(string: "mainAppUrl://")!
// self.extensionContext?.open(url, completionHandler: { (success) in
// if (!success) {
// print("error: failed to open app from Today Extension")
// }
// })
//
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(TodayViewController.doLaunchApp))
self.view.addGestureRecognizer(tapGesture)
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.delegate = self
locationManager.requestAlwaysAuthorization()
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
myLat = locValue.latitude
myLong = locValue.longitude
}
func SendData(){
print("Sent")
takeShot()
var batteryLevel:Float = UIDevice.current.batteryLevel
let Type = 2
let root = "https://demourl.com/locreciver.php?latitude="
let url = "\(root)\(myLat)&longitude=\(myLong)&type=\(Type)&msisdn=\(MSISDN)&battery=\(batteryLevel)";
Alamofire.request(url, method:.post).responseString {
response in
switch response.result {
case .success:
print(response)
case .failure(let error):
print(error)
}
}
}
func takeShot(){
let mapSnapshotOptions = MKMapSnapshotter.Options()
// Set the region of the map that is rendered.
let location = CLLocationCoordinate2DMake(myLat, myLong) // Apple HQ
let region = MKCoordinateRegion(center: location, latitudinalMeters: 900, longitudinalMeters: 900)
mapSnapshotOptions.region = region
// Set the scale of the image. We'll just use the scale of the current device, which is 2x scale on Retina screens.
mapSnapshotOptions.scale = UIScreen.main.scale
// Set the size of the image output.
mapSnapshotOptions.size = CGSize(width: mapImage.frame.width, height: mapImage.frame.height)
// Show buildings and Points of Interest on the snapshot
mapSnapshotOptions.showsBuildings = true
mapSnapshotOptions.showsPointsOfInterest = true
let snapShotter = MKMapSnapshotter(options: mapSnapshotOptions)
snapShotter.start { (snapshot:MKMapSnapshotter.Snapshot?, NSError) in
let image = snapshot?.image
var annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: self.myLat, longitude: self.myLong)
annotation.title = "Your Title"
let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "annotation")
let pinImage = UIImage(named: "pin")
print(pinImage)
UIGraphicsBeginImageContextWithOptions(image!.size, true, image!.scale);
image?.draw(at: CGPoint(x: 0, y: 0)) //map
pinImage!.draw(at: (snapshot?.point(for: annotation.coordinate))!)
annotationView.drawHierarchy(in: CGRect(x: snapshot!.point(for: annotation.coordinate).x, y: (snapshot?.point(for: annotation.coordinate).y)!, width: annotationView.frame.size.width-100, height: annotationView.frame.size.height-100), afterScreenUpdates: true)
let finalImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// completion(image: finalImage, error: nil)
self.mapImage.image = finalImage
//self.mapImage.image = snapshot?.image
}
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
if let userDefaults = UserDefaults(suiteName: "group.com.demo.spoton") {
var value1 = userDefaults.bool(forKey: "IsLogin")
if value1 == true {
print (value1)
MSISDN = userDefaults.string(forKey: "MSISDN")!
lblTitle.text = "Service Active"
let timer1 = Timer.scheduledTimer(withTimeInterval: 1500.0, repeats: false) { (timer) in
self.SendData()
}
let timer2 = Timer.scheduledTimer(withTimeInterval: 1500.0, repeats: true) { (timer) in
print("Looop")
self.SendData()
}
}
else {
lblTitle.text = "SpotOn Service InActive. Needs Login"
}
}
completionHandler(NCUpdateResult.newData)
}
}

How to make the annotation appear on the Apple map via Swift?

So basically, I'm calling a Rest API to get all Bus Stops location, then put annotation of all bus stops within 5km from my current location on the map when a button is called. However, it is just not displaying, I can't seem to figure out the problem.
import UIKit
import MapKit
class MapKitViewController: UIViewController, CLLocationManagerDelegate {
#IBOutlet weak var GPSButton: UIButton!
var stopSearchResults: [Value] = []
var Annotations: [BusStopAnnotation] = []
let queryServices = QueryService()
let locationManager:CLLocationManager = CLLocationManager()
#IBOutlet weak var mapView: MKMapView!
var currentLocation: CLLocationCoordinate2D?
var counter: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
UIApplication.shared.isNetworkActivityIndicatorVisible = true
queryServices.GetAllBusStops(){
result in
UIApplication.shared.isNetworkActivityIndicatorVisible = false
if let result = result {
self.stopSearchResults = result.value
}
}
configureLocationService()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
private func configureLocationService() {
locationManager.delegate = self
let status = CLLocationManager.authorizationStatus()
if status == .notDetermined {
locationManager.requestAlwaysAuthorization()
} else if status == .authorizedAlways || status == .authorizedWhenInUse {
beginLocationUpdate(locationManager: locationManager)
}
}
private func beginLocationUpdate(locationManager: CLLocationManager) {
mapView.showsUserLocation = true
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
}
private func zoomToLatestLocation(with coordinate: CLLocationCoordinate2D) {
let zoomRegion = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
mapView.setRegion(zoomRegion, animated: true)
}
#IBAction func GPSTrack(_ sender: Any) {
InputAllAnnotation(busStops: stopSearchResults)
print("Searching for nearby bus stops")
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
print("Did get latest location")
guard let latestLocation = locations.first else { return }
if currentLocation == nil {
zoomToLatestLocation(with: latestLocation.coordinate)
}
currentLocation = latestLocation.coordinate
}
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
print("The status changed")
if status == .authorizedAlways || status == .authorizedWhenInUse {
beginLocationUpdate(locationManager: manager)
}
}
func InputAllAnnotation(busStops: [Value]) {
for busStop in busStops{
let busStopObj = BusStopAnnotation(value: busStop)
Annotations.append(busStopObj)
let distance = busStop.GetDistance(latitude: Double(currentLocation?.latitude ?? 0), longitude: Double(currentLocation?.longitude ?? 0))
if distance < 5000 {
mapView.addAnnotation(busStopObj)
}
}
}
}
extension MapKitViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let busStopAnnotation = mapView.dequeueReusableAnnotationView(withIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier) as?
MKMarkerAnnotationView {
busStopAnnotation.animatesWhenAdded = true
busStopAnnotation.titleVisibility = .adaptive
busStopAnnotation.canShowCallout = true
return busStopAnnotation
}
return nil
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
print("The annotation was selected: \(String(describing: view.annotation?.title))")
}
}
final class BusStopAnnotation: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
var subtitle: String?
var busStopCode: String?
init(value : Value) {
self.coordinate = value.GetLocationCoordinate2D()
self.title = value.roadName
self.subtitle = value.description
self.busStopCode = value.busStopCode
}
init(coordinate: CLLocationCoordinate2D, roadName: String?, description: String?, busStopCode: String?) {
self.coordinate = coordinate
self.title = roadName
self.subtitle = description
self.busStopCode = busStopCode
}
var region: MKCoordinateRegion {
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
return MKCoordinateRegion(center: coordinate, span: span)
}
}
You may need
self.mapView.delegate = self
import:
import UIKit
import MapKit
set class
class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {
outlet your map
#IBOutlet weak var map: MKMapView!
Code:
let customPin : CLLocationCoordinate2D = CLLocationCoordinate2DMake(Latitude, Longitude)
let objectAnnotation = MKPointAnnotation()
objectAnnotation.coordinate = customPin
objectAnnotation.title = "Here's your custom PIN"
self.map.addAnnotation(objectAnnotation)
extra:
to set the camera near the PIN
let theSpan:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: 0.009, longitudeDelta: 0.009)
let pointLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(Latitude, Longitude)
let region:MKCoordinateRegion = MKCoordinateRegion(center: pointLocation, span: theSpan)
self.map.setRegion(region, animated: true)
move values depending how close/far you want the camera
let theSpan:MKCoordinateSpan = MKCoordinateSpan(latitudeDelta: HERE, longitudeDelta: HERE)

How to change MapKit's pin color in Swift 3/4?

So, I am creating an app with MapKit. So, I need some help of changing the maps pin color from red to any color possible. I tried every way, I can't just not find a solution. Can any one check my code, and help me apply it to my code below of changing the map's pin tintColor. Thanks in advance.
Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.requestWhenInUseAuthorization()
}
extension ViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if locations.count > 0 {
let location = locations.last!
print("Accuracy: \(location.horizontalAccuracy)")
if location.horizontalAccuracy < 100 {
manager.stopUpdatingLocation()
let span = MKCoordinateSpan(latitudeDelta: 0.014, longitudeDelta: 0.014)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.region = region
let location = CLLocation(latitude: latitude, longitude: longitude)
let place = Place(location: location, reference: reference, name: name, address: address)
self.places.append(place)
let annotation = MyHome(location: place.location!.coordinate, title: place.placeName)
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
}
}
}
}
}
}
}
}
}
MyHome CLass
import Foundation
import MapKit
class MyHome: NSObject, MKAnnotation {
let coordinate: CLLocationCoordinate2D
let title: String?
init(location: CLLocationCoordinate2D, title: String) {
self.coordinate = location
self.title = title
super.init()
}
}
MapView
fileprivate var locationManager = CLLocationManager()
fileprivate var heading: Double = 0
fileprivate var interactionInProgress = false
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
{
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required public init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
}
open override func viewDidLoad()
{
super.viewDidLoad()
self.mapView.isRotateEnabled = false
if let annotations = self.annotations
{
addAnnotationsOnMap(annotations)
}
locationManager.delegate = self
}
open override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
locationManager.startUpdatingHeading()
}
open override func viewDidDisappear(_ animated: Bool)
{
super.viewDidDisappear(animated)
locationManager.stopUpdatingHeading()
}
open func addAnnotations(_ annotations: [ARAnnotation])
{
self.annotations = annotations
if self.isViewLoaded
{
addAnnotationsOnMap(annotations)
}
}
fileprivate func addAnnotationsOnMap(_ annotations: [ARAnnotation])
{
var mapAnnotations: [MKPointAnnotation] = []
for annotation in annotations
{
if let coordinate = annotation.location?.coordinate
{
let mapAnnotation = MKPointAnnotation()
mapAnnotation.coordinate = coordinate
let text = String(format: "%#, AZ: %.0f, VL: %i, %.0fm", annotation.title != nil ? annotation.title! : "", annotation.azimuth, annotation.verticalLevel, annotation.distanceFromUser)
mapAnnotation.title = text
mapAnnotations.append(mapAnnotation)
}
}
mapView.addAnnotations(mapAnnotations)
mapView.showAnnotations(mapAnnotations, animated: false)
}
open func locationManager(_ manager: CLLocationManager, didUpdateHeading newHeading: CLHeading)
{
heading = newHeading.trueHeading
if(!self.interactionInProgress && CLLocationCoordinate2DIsValid(mapView.centerCoordinate))
{
let camera = mapView.camera.copy() as! MKMapCamera
camera.heading = CLLocationDirection(heading);
self.mapView.setCamera(camera, animated: false)
}
}
open func mapView(_ mapView: MKMapView, regionWillChangeAnimated animated: Bool)
{
self.interactionInProgress = true
}
open func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool)
{
self.interactionInProgress = false
}
}
you have to change its color in MKMapViewDelegate delegate method
#IBOutlet weak var customMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let anoot = MKPointAnnotation()
anoot.coordinate = CLLocationCoordinate2D.init(latitude: lat, longitude: lng)
customMap.addAnnotation(anoot)
customMap.delegate = self
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView.pinTintColor = UIColor.green
return annotationView
}
you can do it in your code like this
class ViewController: UIViewController {
var locationManager = CLLocationManager()
#IBOutlet weak var yourMap: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
yourMap.delegate = self
let anoot = MKPointAnnotation()
anoot.coordinate = CLLocationCoordinate2D.init(latitude: 23.0225, longitude: 72.5714)
yourMap.addAnnotation(anoot)
yourMap.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.requestWhenInUseAuthorization()
}
}
extension ViewController: CLLocationManagerDelegate, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView.pinTintColor = UIColor.green
return annotationView
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if locations.count > 0 {
let location = locations.last!
print("Accuracy: \(location.horizontalAccuracy)")
if location.horizontalAccuracy < 100 {
manager.stopUpdatingLocation()
let span = MKCoordinateSpan(latitudeDelta: 0.014, longitudeDelta: 0.014)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
yourMap.region = region
// let location = CLLocation(latitude: latitude, longitude: longitude)
// let place = Place(location: location, reference: reference, name: name, address: address)
// self.places.append(place)
//
// let annotation = MyHome(location: place.location!.coordinate, title: place.placeName)
//
// DispatchQueue.main.async {
//
// self.mapView.addAnnotation(annotation)
// }
}
}
}
}

Resources