Swift Using NotificationCenter to Pass Data Between View Controllers - swift

I am trying to pass the data of user-input coordinates from one VC to another using Notifications. When I run the code, it does not add an annotation onto the MapView, so I have a hunch I may have not set up the notification to send with the inputted coordinates properly, but I am not sure where I have went wrong.
Class file that takes the input of coordinates:
import UIKit
import CoreLocation
var locations: [Dictionary<String, Any>] = [] // here I initialize my empty array of locations
class OtherVC: UIViewController {
#IBOutlet weak var latitudeField: UITextField!
#IBOutlet weak var longitudeField: UITextField!
#IBOutlet weak var titleTextField: UITextField!
var coordinates = [CLLocationCoordinate2D]()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addToMap(_ sender: Any) {
let lat = latitudeField.text!
let long = longitudeField.text!
let title = titleTextField.text!
var location: [String: Any] = ["title": title, "latitude": lat, "longitude": long]
locations.append(location)
NotificationCenter.default.post(name: NSNotification.Name("MapViewController.coordinate.updated"), object: locations, userInfo: nil)
}
}
Class file that receives notification and places annotation:
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
}
func add(notification: NSNotification) {
let dict = notification.object as! NSDictionary
// takes the coordinates from the notification and converts such that the map can interpret them
let momentaryLat = (dict["latitude"] as! NSString).doubleValue
let momentaryLong = (dict["longitude"] as! NSString).doubleValue
let annotation = MKPointAnnotation()
annotation.title = dict["title"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: momentaryLat as CLLocationDegrees, longitude: momentaryLong as CLLocationDegrees)
mapView.addAnnotation(annotation)
self.mapView.centerCoordinate = annotation.coordinate
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "pinAnnotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
}
annotationView?.annotation = annotation
return annotationView
}
}

It's the same as the Objective-C API, but uses Swift's syntax.
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: #selector(batteryLevelChanged),
name: UIDeviceBatteryLevelDidChangeNotification,
object: nil)
Or in Swift 3:
NotificationCenter.default.addObserver(
self,
selector: #selector(self.batteryLevelChanged),
name: .UIDeviceBatteryLevelDidChange,
object: nil)
If your observer does not inherit from an Objective-C object, you must prefix your method with #objc in order to use it as a selector.
#objc func batteryLevelChanged(notification: NSNotification){
//do stuff
}

Register obeserver to receive notification in your MapViewController
override func viewDidLoad() {
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.add(_:)), name: NSNotification.Name("MapViewController.coordinate.updated"), object: nil)
}
Also you can send data using userInfo property of NSNotification or send using custom object.
func add(_ notification: NSNotification) {
...
}
see pass data using nsnotificationcenter

Related

Error showing customs anotation in maps swift

I'm trying to show some custom anotation I can see the custom picture in its place but the text disappears and reapear every time I zoom in or out. I've checked several similar question but I don't know how to apply to my code. So please help!
import UIKit
import MapKit
class LocalizacionViewController: UIViewController,MKMapViewDelegate {
var evento: Evento?
#IBOutlet weak var actIndicator: UIActivityIndicatorView!
#IBOutlet weak var contenedorView: UIView!
#IBOutlet weak var mkMapa: MKMapView!
override func viewDidLoad() {
mkMapa.delegate = self
super.viewDidLoad()
evento = eventos.eventoPulsado()
}
override func viewWillAppear(_ animated: Bool) {
contenedorView.layer.borderWidth = 2
contenedorView.aplicarFondoDegradado()
contenedorView.layer.borderColor = UIColor.white.cgColor
contenedorView.layer.cornerRadius = 10
customPin()
let initialLocation = CLLocationCoordinate2D(latitude: (evento!.lat)!, longitude: (evento!.long)!)
mkMapa.setCenter(initialLocation, animated: true)
let region = MKCoordinateRegion(center: initialLocation,latitudinalMeters: 2000,longitudinalMeters: 2000)
mkMapa.setRegion(region, animated: true)
}
func mapViewDidFinishLoadingMap(_ mapView: MKMapView) {
actIndicator.isHidden = true
actIndicator.stopAnimating()
}
func customPin(){
let addAnotation = MKPointAnnotation()
addAnotation.title = evento!.nombre
addAnotation.coordinate = CLLocationCoordinate2D(latitude: evento!.lat!, longitude: evento!.long!)
self.mkMapa.addAnnotation(addAnotation)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "custom")
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "custom")
} else {
annotationView?.annotation = annotation
}
annotationView?.image = UIImage(named:"bars.png" )
return annotationView
}
}

How can I use UIlabel.text in other controller?

I have two Controller which is Map and BottomSheet.
Once i click marker in Map I want to change UIlabel text.
BottomsheetController.swift
var Data : [DataClass] = []
class BottomSheetViewController : UIViewController{
#IBOutlet weak var Id: UILabel!
func getid(id : String?){
let kick = id
self.Id.text = kick
}
}
MapViewController.swift
class MapViewController: UIViewController ,GMUClusterManagerDelegate{
#IBOutlet weak var mapView: GMSMapView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
addBottomSheetView()
}
// MARK: Click marker
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if let poiitem = marker.userData as? POIItem {
let bvc = BottomSheetViewController()
bvc.getimei(imei: Data[0].id)
}
return true
}
}
it keeps saying Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Just use segue for passing data between two controller
performSegue(idnitfierName,sender)
And than,
perform(){
let vc = segue.destination as! MapViewController
vc.variableName = variable That you have to pass
}

Swift MKMapViewDelegate - Transferring coordinates between view controllers

I am trying to construct a Map App that can receive user inputs of latitude and longitude coordinates that when entered, will place a pin on a map in a different tab. My FirstVC consists of a button "Add Locations" that segues to OtherVC which the user can input the coordinates. SecondVC consists of the MapView. My initial idea is to have a specific array of coordinates, and any new coordinates will be appended to this array. The execution is where I am lacking, because I am not sure how to transfer this array to the MapView. Here is what I have so far:
For the input of coordinates:
import UIKit
import CoreLocation
class OtherVC: UIViewController {
#IBOutlet weak var latitudeField: UITextField!
#IBOutlet weak var longitudeField: UITextField!
var coordinates = [CLLocationCoordinate2D]()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func addToMap(_ sender: Any) {
let lat = Double(latitudeField.text!)
let long = Double(longitudeField.text!)
self.coordinates.append(CLLocationCoordinate2D(latitude: lat!, longitude: long!))
}
}
For the MapView:
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var coordinates = [CLLocationCoordinate2D]() {
didSet {
// Update the pins
// Since it doesn't check for which coordinates are new, it you go back to
// the first view controller and add more coordinates, the old coordinates
// will get a duplicate set of pins
for (index, coordinate) in self.coordinates.enumerated() {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "Location \(index)"
mapView.addAnnotation(annotation)
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "pinAnnotation"
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
}
annotationView?.annotation = annotation
return annotationView
}
}
I think you need is get your second ViewController MapViewController from your tabBarController and then pass the coordinates array, so in your addToMap Action replace with this
#IBAction func addToMap(_ sender: Any) {
let lat = Double(latitudeField.text!)
let long = Double(longitudeField.text!)
self.coordinates.append(CLLocationCoordinate2D(latitude: lat!, longitude: long!))
//here we pass the coordinate array to mapViewController
if let mapViewController = self.tabBarController?.viewControllers?[1] as? MapViewController
{
mapViewController.coordinates = self.coordinates
}
}
You need also add a navigation controller, like in the picture
I hope this helps you
Generally speaking I only use the method of directly passing the data from one ViewController to the next if there is a parent child relationship and I can do it in prepareForSegue or in and unwind (child to parent). Otherwise I think its better to use the publisher subscriber model with Notification. When your coordinate changes you post a Notification:
NotificationCenter.default.post(name: NSNotification.Name("MapViewController.coordinate.updated"), object: self, userInfo: nil)
Now anyone who cares about MapViewController's coordinates changing can listen:
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(coordinateUpdated), name: NSNotification.Name("coordinate.updated"), object: nil)
}
deinit {
NotificationCenter.default.removeObserver(self)
}
#objc private func coordinateUpdated( notification: Notification) {
if let source = notification.object as? MapViewController {
print(source.coordinates)
}
}
This makes the views loosely coupled and MapViewController doesn't need to care about who needs to be updated; the subscribers are responsible for registering themselves.

Swift- segue between views with map annotation button

I relatively new to programming and need help switching between views by tapping a button in a mapView annotated pin.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
//REF
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var bottompnl: UIImageView!
#IBAction func mysticsbtn(sender: AnyObject) {
}
#IBAction func bondibtn(sender: AnyObject) {
}
//MAP
let locationManager = CLLocationManager()
//Annotation
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
let identifier = "beach"
if annotation is Beach {
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(identifier)
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView!.canShowCallout = true
let btn = UIButton(type: .DetailDisclosure)
annotationView!.rightCalloutAccessoryView = btn
} else {
annotationView!.annotation = annotation
}
return annotationView
self.performSegueWithIdentifier("mysticssegue", sender: nil)
}
return nil
}
override func viewDidLoad() {
super.viewDidLoad()
let annotations = getMapAnnotations()
// Add mappoints to Map
mapView.addAnnotations(annotations)
mapView.delegate = self
//MAP
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
//MAKES THE DOT
self.mapView.showsUserLocation = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MAP: Location Delegates Methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let centre = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: centre, span: MKCoordinateSpan(latitudeDelta: 3, longitudeDelta: 3))
self.mapView.setRegion(region, animated: true)
self.locationManager.startUpdatingLocation()
}
//TO FIND ERROS
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Erros: " + error.localizedDescription)
}
//MARK:- Annotations
func getMapAnnotations() -> [Beach] {
var annotations:Array = [Beach]()
//load plist file
var beaches: NSArray?
if let path = NSBundle.mainBundle().pathForResource("beaches", ofType: "plist") {
beaches = NSArray(contentsOfFile: path)
}
if let items = beaches {
for item in items {
let lat = item.valueForKey("lat") as! Double
let long = item.valueForKey("long")as! Double
let annotation = Beach(latitude: lat, longitude: long)
annotation.title = item.valueForKey("title") as? String
annotations.append(annotation)
}
}
return annotations
}
//END
}
It looks like you're returning from the function mapView before calling performSegueWithIdentifier.
return annotationView
self.performSegueWithIdentifier("mysticssegue", sender: nil)
Try reversing the order of these two lines. I also noticed that you have some #IBAction methods that have no code in them.

-swift- trouble passing parse object from map kit annotation to detailViewController using disclosure button in swift

Please help, Im having trouble passing parse object from annotation Query to destinationViewController. When the user taps the annotation view it takes the user to the destinationVC, but it passes the same object no matter which annotation the user taps. I think it has something to do with the prepareForSegue function. Please let me know what Im doing wrong.
Heres my code:
class MapSearchViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var mapView: MKMapView!
var jobPosts = [PFObject]()
var jobPost: PFObject?
let locationManager = CLLocationManager()
var currentLoc: PFGeoPoint! = PFGeoPoint()
override func viewDidLoad() {
super.viewDidLoad()
//MARK new added code Start--
self.mapView.delegate = self
self.mapView.setUserTrackingMode(MKUserTrackingMode.Follow, animated: true)
//MARK new added code End--
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
//Snippet For Blue Current Location Dot:
//self.mapView.showsUserLocation = true
}
//MARK: -- Location Delegate Methods
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.last
let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1))
self.mapView.setRegion(region, animated: true)
self.locationManager.stopUpdatingLocation()
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Errors: " + error.localizedDescription)
}
//MARK: OCT 26
override func viewDidAppear(animated: Bool) {
let annotationQuery = PFQuery(className: "JobPost")
currentLoc = PFGeoPoint(location: locationManager.location)
annotationQuery.whereKey("location", nearGeoPoint: currentLoc, withinMiles: 100)
annotationQuery.findObjectsInBackgroundWithBlock {
(posts, error) -> Void in
if error == nil {
// The find succeeded.
print("Successful query for annotations")
let jobPosts = posts as [PFObject]!
for post in jobPosts {
let point = post["location"] as! PFGeoPoint
var annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude)
self.mapView.addAnnotation(annotation)
annotation.title = post["job"] as! String
let pay = post["price"] as! String
let payLabel = "\(pay)$"
annotation.subtitle = payLabel
let btn = UIButton(type: .DetailDisclosure)
self.jobPost = post
}
} else {
// Log details of the failure
print("Error: \(error)")
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
var view = mapView.dequeueReusableAnnotationViewWithIdentifier("annotationIdentifier")
if view == nil {
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "annotationIdentifier")
view?.canShowCallout = true
view?.rightCalloutAccessoryView = UIButton(type: .DetailDisclosure)
} else {
view?.annotation = annotation
}
return view
}
var selectedAnnotation: MKPointAnnotation!
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
selectedAnnotation = view.annotation as? MKPointAnnotation
performSegueWithIdentifier("annotationSender", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if let destination = segue.destinationViewController as? JobDetailViewController {
destination.currentObject = jobPost
}
}
}
code for detailVC:
class JobDetailViewController: UIViewController, MFMailComposeViewControllerDelegate {
#IBOutlet weak var jobTitleLabel: UILabel!
#IBOutlet weak var priceLabel: UILabel!
#IBOutlet weak var distanceLabel: UILabel!
#IBOutlet weak var jobDescriptionContent: UITextView!
#IBOutlet weak var pictureImageView: UIImageView!
var jobPosts:[PFObject]!
var currentObject : PFObject?
var distance = "7"
var annotation = MKPointAnnotation()
override func viewDidLoad() {
super.viewDidLoad()
self.distanceLabel.text = "\(distance) miles away"
if let object = currentObject {
jobTitleLabel.text = object["job"] as! String
priceLabel.text = object["price"] as! String
jobDescriptionContent.text = object["description"] as! String
}
}