3

i'm new in swift programming. I want when user tap a MKAnnotationPoint to move on the next view controller. The way i do it now is by pressing the Button "Button" as you see at the top of image1. My code:

mapViewController.swift

import UIKit
import MapKit
import CoreLocation

class mapViewController: UIViewController, UITextFieldDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate, MKMapViewDelegate {

    // MARK: Properties
    let pin = UIImage(named: "pin")
    var annotationTouched = String()
    var viaSegue = MKAnnotationView()

    // MARK: MAP
    @IBOutlet weak var mapView: MKMapView!


    let coreLocationManager = CLLocationManager()
    let locations = LocationList().Location

    // all locations will be stored on this array
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
    {
        let location = locations [0]

        //map zoomed
        let span:MKCoordinateSpan = MKCoordinateSpanMake(0.003, 0.003)
        //users location
        let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)

        let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
        mapView.setRegion(region, animated: true)

        self.mapView.showsUserLocation = true
    }

    // func to change red pin to my custom pin
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
    {

        if let annotation = annotation as? Locations{
            if let view = mapView.dequeueReusableAnnotationView(withIdentifier: annotation.identifier){
                return view
            }else{
                let view = MKAnnotationView(annotation: annotation, reuseIdentifier: annotation.identifier)
                view.image = pin
                view.isEnabled = true
                view.canShowCallout = true
                //view.leftCalloutAccessoryView = UIImageView(image: pin)
                let btn = UIButton(type: .detailDisclosure)
                view.rightCalloutAccessoryView = btn

                return view
            }
        }
        return nil
    }

    // assigning the pin that is selected in the map to the annotation touched variable
    func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView)
    {
        if let annotation = view.annotation as? Locations {
            annotationTouched = annotation.title ?? "No title"
        }
    }


    override func viewDidLoad()
    {
        super.viewDidLoad()

        coreLocationManager.delegate = self
        //desired accuracy is the best accuracy, very accurate data for the location
        coreLocationManager.desiredAccuracy = kCLLocationAccuracyBest
        //request authorization from the user when user using my app
        coreLocationManager.requestWhenInUseAuthorization()

        coreLocationManager.startUpdatingLocation()

        mapView.delegate = self

        mapView.addAnnotations(locations)
    }

    // passing the name of the place on the next view controller which is the review view controller (RateViewController)
    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
        let destViewController : RateViewController = segue.destination as! RateViewController
        destViewController.placeLabelString = annotationTouched

    }


}

Locations.swift

import UIKit
import MapKit

class Locations: NSObject, MKAnnotation {
    // required coordinate, title, and the reuse identifier for this annotation
    var identifier = "locations"
    var title: String?
    var coordinate: CLLocationCoordinate2D
    //initializer taking a name, a latitude and longitude to populate the title and coordinate for each instance of this object
    init(name:String,lat:CLLocationDegrees,long:CLLocationDegrees){
        title = name
        coordinate = CLLocationCoordinate2DMake(lat, long)
    }

}
// Creating the list of the places that will be pinned in map
class LocationList: NSObject {
    var Location = [Locations]()
    override init(){
        Location += [Locations(name: "Dio Con Dio", lat: 40.590130, long: 23.036610)]
        Location += [Locations(name: "Paradosiako - Panorama", lat: 40.590102, long:23.036180)]
        Location += [Locations(name: "Veranda",  lat: 40.607740, long: 23.103044)]
        Location += [Locations(name: "Markiz",  lat: 40.634252, long: 22.936276)]
        Location += [Locations(name: "Moi Lounge Bar",  lat: 40.653481, long: 22.994131)]
        Location += [Locations(name: "Boulevard Lounge Bar",  lat: 40.658462, long: 22.983198)]
        Location += [Locations(name: "Ernést Hébrard",  lat: 40.631829, long: 22.941014)]
        Location += [Locations(name: "Tribeca - All Day & Night Bar",  lat: 40.631029, long: 22.942396)]

    }
}

image1

image2

Rashwan L
  • 38,237
  • 7
  • 103
  • 107
Roula Chiouma
  • 151
  • 1
  • 10

2 Answers2

2

Create a segue from your mapViewController to your destination viewController and then do it like this:

func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
    self.performSegue(withIdentifier: "destViewController", sender: nil)
}

Here is a sample project I created that shows you how it works.

To edit the segue identifier do this: enter image description here

Rashwan L
  • 38,237
  • 7
  • 103
  • 107
  • how do you find your `withIdentifier:` value? – Roula Chiouma May 14 '17 at 10:55
  • @RoulaChiouma, you set it in your StoryBoard by clicking on the segue and then on the right side menu you have the identifier. See the image in my post that I just added. – Rashwan L May 14 '17 at 10:57
  • Yes, but on the example, you have another value there. that's why i ask! You viewcontroller name is `DestinationViewController` and the code you have `ShowDestinationViewController` – Roula Chiouma May 14 '17 at 11:04
  • My second viewControllers name is `DestinationViewController` and the segue identifier from from my `MapViewController` to my `DestinationViewController` is called `ShowDestinationViewController`. Is this the answer to your question? I´m not completely sure I understad the question. – Rashwan L May 14 '17 at 11:07
  • Yes ! and how do you add this Storyboard Segue? i tried ctrl + mouse like the way i do that on buttons, but cant do it. – Roula Chiouma May 14 '17 at 11:23
  • Ok i made it! Can i make the same thing if the user touch the `.canShowCallout` ? – Roula Chiouma May 14 '17 at 12:52
  • Sure you can by adding a `tapGesture` checkout [this](http://stackoverflow.com/questions/3395772/detect-tap-on-calloutbubble-in-mkannotationview) post of how to do that. Good luck! – Rashwan L May 14 '17 at 19:02
  • it is in obj-c...! any example with swift? – Roula Chiouma May 14 '17 at 21:42
  • @RoulaChiouma it is not recommended using a tap gesture on a call out when you can just use a button. Look at my answer below, it shows you how to do it. – Tristan Beaton May 14 '17 at 22:07
1

Having a button separate to the map view seems like an inappropriate way of doing something like this. You should use a button on the annotation view, like the detail item you have. This code below will segue to the add review controller when the user presses the detail button on the annotation view. You'll need to change MKPointAnnotation to your Locations, since you haven't provided the code for that custom class.

import UIKit
import MapKit
import CoreLocation

class MapViewController: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate {


    @IBOutlet weak var mapView: MKMapView!

    let locationManager: CLLocationManager = {

        let manager = CLLocationManager()

        manager.requestWhenInUseAuthorization()

        manager.desiredAccuracy = kCLLocationAccuracyBest

        manager.startUpdatingLocation()

        return manager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.

        self.locationManager.delegate = self

        self.mapView.addAnnotations(LocationList().Location)
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

        // When the user's location updates, the map will be recentered on the user. This does the same as all your code.

        self.mapView.setUserTrackingMode(.follow, animated: true)
    }

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

        if let pin = annotation as? Locations {

            let view = mapView.dequeueReusableAnnotationView(withIdentifier: "pin") ?? MKPinAnnotationView(annotation: pin, reuseIdentifier: "pin")

            view.canShowCallout = true

            view.image = UIImage(named: "pin")

            let reviewButton = UIButton(type: .detailDisclosure)

            reviewButton.addTarget(self, action: #selector(self.addReview), for: .touchUpInside)

            view.rightCalloutAccessoryView = reviewButton

            return view
        }

        return nil
    }

    func addReview(){
        // This gets called with the detail declosure button is pressed.

        // This gets the selected pin.
        if let pin = self.mapView.selectedAnnotations.first as? Locations { 
            // This gets the view controller from your storyboard
            guard let vc = self.storyboard?.instantiateViewController(withIdentifier: "ReviewViewController") as? ReviewViewController else { return }

            // This passes the locations pin to the review view controller
            vc.pin = pin

            // This shows the review view controller
            self.navigationController?.pushViewController(vc, animated: true)
        }
    }
}

class ReviewViewController: UIViewController {

    @IBOutlet weak var locationLabel: UILabel!

    @IBOutlet weak var reviewTextField: UITextField!

    var pin: Locations!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.navigationItem.title = "Write a Review"

        self.locationLabel.text = pin.title

        let sendButton = UIBarButtonItem(title: "Send", style: .done, target: self, action: #selector(self.submitReview))

        self.navigationItem.rightBarButtonItem = sendButton
    }

    func submitReview(){

        print("Your review is: \(self.reviewTextField.text ?? "Why didn't you write anything?")")
    }
}

class Locations: NSObject, MKAnnotation {

    var title: String?

    var coordinate: CLLocationCoordinate2D

    init(name:String,lat:CLLocationDegrees,long:CLLocationDegrees){

        title = name

        coordinate = CLLocationCoordinate2DMake(lat, long)
    }
}

class LocationList: NSObject {

    var Location = [Locations]()

    override init(){

        Location += [Locations(name: "Dio Con Dio", lat: 40.590130, long: 23.036610)]
        Location += [Locations(name: "Paradosiako - Panorama", lat: 40.590102, long:23.036180)]
        Location += [Locations(name: "Veranda",  lat: 40.607740, long: 23.103044)]
        Location += [Locations(name: "Markiz",  lat: 40.634252, long: 22.936276)]
        Location += [Locations(name: "Moi Lounge Bar",  lat: 40.653481, long: 22.994131)]
        Location += [Locations(name: "Boulevard Lounge Bar",  lat: 40.658462, long: 22.983198)]
        Location += [Locations(name: "Ernést Hébrard",  lat: 40.631829, long: 22.941014)]
        Location += [Locations(name: "Tribeca - All Day & Night Bar",  lat: 40.631029, long: 22.942396)]
    }
}
Tristan Beaton
  • 1,742
  • 2
  • 14
  • 25
  • i edited that! I added the custom class! but i think that your code is too complicated. There must be an easiest way to click the detail item to move on the next view controller. – Roula Chiouma May 14 '17 at 14:07
  • I have simplified it more, but this isn't complicated. The `addReview` function is the only thing I've added. I have also recreated your other view controller to test it, but it is just that function that you need. – Tristan Beaton May 14 '17 at 21:05
  • @TristanBeaton do I understand correctly that your code will open the next view controller if the info icon on the right side is pressed? If so, is there any way to get the same effect when pressing on the text itself? – dopexxx Apr 09 '21 at 21:48
  • 1
    @dopexxx I think you are correct. You could add a tap gesture to the MKAnnotationView which would detect a tap anywhere with that view including the text. But I don't know if there is a way to just have the text clickable without creating a custom annotation view. – Tristan Beaton Apr 11 '21 at 01:15