I am working on an App where you can see some pins/annotations and when you click them they show some information about that place. What I want to do is add a button so when the user clicks the "callout" it activates a segue which will open a new View with more detailed information.
I have my code and functions but it seems to be an error, because when I click the button to run the segue, the App crashes. My code is the following...
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
var pin = MKPointAnnotation()
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.showsUserLocation = true
//Adding information to my Annotation
let pinCoordenadas = CLLocationCoordinate2DMake(25.589339000, -100.257724800)
pin.coordinate = pinCoordenadas
pin.title = "Christus Muguerza"
mapView.addAnnotation(pin)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is MKPointAnnotation) {
print("Not MKPointAnnotation")
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "identifier")
if annotationView == nil{
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "identifier")
annotationView!.canShowCallout = true
annotationView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
else {
annotationView!.annotation = annotation
}
annotationView!.image = UIImage(named: "curz")
return annotationView
}
var anotacionSeleccionada : MKPointAnnotation!
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
anotacionSeleccionada = view.annotation as? MKPointAnnotation
performSegue(withIdentifier: "vista", sender: self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? ViewController {
destination.pin = anotacionSeleccionada
}
}
}
The errors...
I hope you guys can help me out, I would really appreciate your help. Thank you
Your outlet to your mapView seem to be broken, since your program crashes on the first row in viewDidLoad. Do this to solve this issue:
Go to your storyboard
Right click on your map
Remove the outlet/outlets if you have by mistake created more than one
Remove #IBOutlet weak var mapView: MKMapView! from your code
Create a new outlet
Run your code again
The issue was that OP had both the mainView and the detailView connected to the file viewController. I created a new file for OP called DetailController and linked the second viewController and now it´s working.
Related
class MapViewController: UIViewController, MKMapViewDelegate, HomeModelProtocol {
var feedItems: NSArray = NSArray()
var selectedLocation : LocationModel = LocationModel()
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let initialLocation = CLLocation(latitude: 45.444958, longitude: 12.328463)
centerMapLocation(location: initialLocation)
mapView.delegate = self
let homeModel = HomeModel()
homeModel.delegate = self
homeModel.downloadItems()
}
func itemsDownloaded(items: NSArray) {
feedItems = items
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
let regionRadus: CLLocationDistance = 1000
func centerMapLocation(location: CLLocation){
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate, regionRadus, regionRadus)
mapView.setRegion(coordinateRegion, animated: true)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
//checkLocationAuthorizationStatus()
displayLocations()
}
func displayLocations(){
let i = feedItems.count
var x = 0
while x<i{
let item: LocationModel = feedItems[x] as! LocationModel
var poiCoodinates = CLLocationCoordinate2D()
poiCoodinates.latitude = CDouble(item.latitude!)!
poiCoodinates.longitude = CDouble(item.longitude!)!
let pin: MKPointAnnotation = MKPointAnnotation()
pin.coordinate = poiCoodinates
self.mapView.addAnnotation(pin)
pin.title = item.name
pin.subtitle = item.address
x = x+1
}
//return loc
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let view = MKMarkerAnnotationView(annotation: selectedLocation as? MKAnnotation, reuseIdentifier: "pin")
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.leftCalloutAccessoryView = UIButton(type: .detailDisclosure)
return view
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
print(control.tag)
selectedLocation = feedItems[0] as! LocationModel
performSegue(withIdentifier: "InformationSegue", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get reference to the destination view controller
let detailVC = segue.destination as! InformationViewController
// Set the property to the selected location so when the view for
// detail view controller loads, it can access that property to get the feeditem obj
detailVC.selectedLocation = selectedLocation
}
}
This is my code.
I want to display the Location in the next Viewcontroller.
I need to get the index at feeditems[].
How can i get the index in:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
So how do i get the index, which Button is tapped. There are many objects that are placed in the map.
Thank you for help and sorry for my bad english, hope you guys understand me.
1.Define Sublass MKPointAnnotation.
class MyPointAnnotation: MKPointAnnotation {
var feedItem: LocationModel
}
2.Set MyPointAnnotation.feedItem to feedItem.
let item: LocationModel = feedItems[x] as! LocationModel
var poiCoodinates = CLLocationCoordinate2D()
poiCoodinates.latitude = CDouble(item.latitude!)!
poiCoodinates.longitude = CDouble(item.longitude!)!
let pin: MyPointAnnotation = MyPointAnnotation()
pin.coordinate = poiCoodinates
pin.feedItem = item // Important!
self.mapView.addAnnotation(pin)
3.Get feedItem in calloutAccessoryControlTapped delegate method.
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if let pin = view.annotation as? MyPointAnnotation {
print(pin.feedItem)
}
}
Sublass MKAnnotation add index property / object from feedItems array to the class and
see custom class MyAnnotation implemented there in swift customPinAnnotationButton
this idea but now i have only objective -c version
- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
NSLog(#"wqwqwqwqwqw . . .");
MyAnnotation*ann = view.annotation;
NSLog(#"nammemmeme : %#",ann.weatherItem);
[self performSegueWithIdentifier:#"showDetails" sender:ann.weatherItem];
}
1.Define subclasses for Annotation
class PointAnnotation: MKPointAnnotation {
var indexAnnotation = 0
}
2.Mapview Delegate
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.isMember(of: MKUserLocation.self) {
return nil
}
let identifier = "myAnnotation"
var annotationView: MKAnnotationView?
annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.image = UIImage(named:"Golf Courses.png")
annotationView?.canShowCallout = true
let callButton = UIButton(type: .detailDisclosure)
annotationView?.rightCalloutAccessoryView = callButton
annotationView?.sizeToFit()
} else {
annotationView!.annotation = annotation
}
}
3.Callaccessory button taped go to next view contoller
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
let obj = kStoryboardShops.instantiateViewController(withIdentifier: "ShopDetailsViewController") as! ShopDetailsViewController
if let annotation = view.annotation as? PointAnnotation {
obj.dicDetails = arrayOfItems[annotation.indexAnnotation]
}
let nav = UINavigationController(rootViewController: obj)
self.present(nav, animated: true, completion: nil)
}
}
I am currently trying to create a map app that has around 20 pins. I have made a segue so that the Pin's Annotation's Title is transferred. However now I need to implement it so that a description is correctly selected from a series of 20 .txt file descriptions and 20 images. But I believe I am able to do it with one Segue.
Here is my current code
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate,CLLocationManagerDelegate {
#IBOutlet weak var MapView: MKMapView!
let manager = CLLocationManager()
var artworkPin:Artwork!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//let location = locations[0]
//let span:MKCoordinateSpan = MKCoordinateSpanMake(0.02, 0.02)
//let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
}
override func viewDidLoad() {
super.viewDidLoad()
// tracking user's location
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
// Setting up Map
let distanceSpan:CLLocationDegrees = 2000
MapView.setRegion(MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(-39.0556253, 174.0752278), distanceSpan, distanceSpan), animated: true)
MapView.showsUserLocation = true
MapView.delegate = self
// artwork on map
let windwandcoord: CLLocationCoordinate2D = CLLocationCoordinate2DMake(-39.055961,174.072288)
artworkPin = Artwork(title:"Wind Wand",locationName:"Majestic",discipline:"Statue",
coordinate:windwandcoord)
MapView.addAnnotation(artworkPin)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MKUserLocation {return nil}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.calloutOffset = CGPoint(x: -5, y: 5)
let calloutButton = UIButton(type: .detailDisclosure)
pinView!.rightCalloutAccessoryView = calloutButton
pinView!.sizeToFit()
}
else {
pinView!.annotation = annotation
}
return pinView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "no", sender:self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let ViewTwo = segue.destination as! ViewTwo
ViewTwo.artworkPin = self.artworkPin
}
}
I also don't think this will give the correct title if I add another pin am I correct? Because it will simply take the name of artworkPin rather than of any variable pin added.
Thanks for any help.
I have created the following:
let artworkPin = Artwork(title:"Wind Wand",locationName:"Majestic",discipline:"Statue",
coordinate:windwandcoord)
where Artwork refers to a class located in Artwork.swift, I am trying to assign a label to obtain the title value (Located in a annotation on UI View 1 ) through a Segue to go to a Label ( Located in UI View 2) by doing the following:
#IBOutlet weak var art_title: UILabel!
var viaSegue = "artwork title should be here"
override func viewDidLoad() {
super.viewDidLoad()
art_title.text = viaSegue
but I don't know how to reference it correctly for via Segue to take the value of "title".
ENTIRE FILE:
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate,CLLocationManagerDelegate {
#IBOutlet weak var MapView: MKMapView!
let manager = CLLocationManager()
var artworkPin = Artwork!
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
//let location = locations[0]
//let span:MKCoordinateSpan = MKCoordinateSpanMake(0.02, 0.02)
//let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude)
}
override func viewDidLoad() {
super.viewDidLoad()
// tracking user's location
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
// Setting up Map
let distanceSpan:CLLocationDegrees = 2000
MapView.setRegion(MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2DMake(-39.0556253, 174.0752278), distanceSpan, distanceSpan), animated: true)
MapView.showsUserLocation = true
MapView.delegate = self
// artwork on map
let windwandcoord: CLLocationCoordinate2D = CLLocationCoordinate2DMake(-39.055961,174.072288)
artworkPin = Artwork(title:"Wind Wand",locationName:"Majestic",discipline:"Statue",
coordinate:windwandcoord)
MapView.addAnnotation(artworkPin)
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MKUserLocation {return nil}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.calloutOffset = CGPoint(x: -5, y: 5)
let calloutButton = UIButton(type: .detailDisclosure)
pinView!.rightCalloutAccessoryView = calloutButton
pinView!.sizeToFit()
}
else {
pinView!.annotation = annotation
}
return pinView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
performSegue(withIdentifier: "no", sender:self)
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let ViewTwo = segue.destination as! ViewTwo
ViewTwo.artworkPin = self.artworkPin
}
}
Thanks for your help
In your vc B add:
var artworkpin: Artwork!
In your vc A:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vcB = segue.destinationViewController as! vcB
vcB.artworkpin = self.artworkpin
}
after that in vc B viewDidLoad you can get the title by art_title.text = artworkpin.title
I've researched on this topic for almost 3 days straight trying to figure out how to present my detail view controller by clicking on the info button of a map annotation in my map view controller. Basically, I can get the annotation to show, but when I click the annotation, nothing happens. I would like to have it present my detail view controller, the same way that when that same item is clicked on in my table view controller, it goes directly to its respective detail view.
Any help would be much, much appreciated!
This is the image of the map annotation I currently have
Below is my code for my MapViewController. I feel like there is something wrong or something is missing in either my prepareForSegue or annotationView functions.
extension MapViewController {
// 1
#objc(mapView:viewForAnnotation:) func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if let annotation = annotation as? Annotations {
let reuseID = "pin"
var view: MKPinAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseID)
as? MKPinAnnotationView { // 2
dequeuedView.annotation = annotation
view = dequeuedView
} else {
// 3
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseID)
view.canShowCallout = true
view.isUserInteractionEnabled = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton.init(type: .detailDisclosure) as UIButton
}
return view
}
return nil
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView {
print(view.annotation?.title)
performSegue(withIdentifier: "moreDetail", sender: self)
}
}
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "moreDetail") {
// pass data to next view
let destViewController:PancakeHouseViewController = segue.destination as! PancakeHouseViewController
destViewController.viaSegue = sender as! MKAnnotationView
}
}
}
I have also included a variable inside my detail view controller (PancakeHouseViewController)... I don't know if it's supposed to be there or not.
var viaSegue = MKAnnotationView()
I believe the problem may just be in the method signature for calloutAccessoryControlTapped. It should be:
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl)
I am rather new to swift and object oriented programming and I am trying to switch views from a view that contains a map to another view by tapping on a right accessory callout button in the annotation of a pin that is dropped by a long press. In the storyboard, I have created a segue between the two views and assigned the segue with the identifier mapseg. Unfortunately, after trying everything I could find via google I cannot get the segue to occur when I tap the right accessory callout button and have no idea why.The application itself is a tabbed application with three tabs. The view for the second tab is the one that contains the map. Also, I don't know if this could have something to do with it, but the view I am trying to transition from is embedded in a navigation controller. Here is my code for the view that I am trying to transition from.
import UIKit
import MapKit
class SecondViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var MapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.MapView.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let reuseID = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseID)
pinView!.canShowCallout = true
pinView!.animatesDrop = true
pinView!.enabled=true
let btn = UIButton(type: .DetailDisclosure)
btn.titleForState(UIControlState.Normal)
pinView!.rightCalloutAccessoryView = btn as UIView
return pinView
}
#IBAction func dropPin(sender: UILongPressGestureRecognizer) {
if sender.state == UIGestureRecognizerState.Began {
let location = sender.locationInView(self.MapView)
let locCoord = self.MapView.convertPoint(location, toCoordinateFromView: self.MapView)
let annotation = MKPointAnnotation()
annotation.coordinate = locCoord
annotation.title = "City Name"
annotation.subtitle = ""
self.MapView.removeAnnotations(MapView.annotations)
self.MapView.addAnnotation(annotation)
self.MapView.selectAnnotation(annotation, animated: true)
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
self.performSegueWithIdentifier("mapseg", sender: self)
}
}
}
}
I think you need to override prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) method. And check whether if working