I'm trying to display pins on a map, with a preset search parameter. On a button click, the app displays all of the local taxi services. That part I have down...I have the items appending to an [MKPointAnnotation], and it successfully displays a populated list in the tableView as well as pins on the mapView. For some reason I can't get the title and subtitle to display when you tap on the pin on the mapView, even when implementing the viewFor annotation: delegate. Any ideas?
class MapViewController: UIViewController, CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var tableView: UITableView!
let locationManager = CLLocationManager()
let regionRadius: CLLocationDistance = 1500
var matchingItems: [MKMapItem] = [MKMapItem]()
var annotationGroup = [MKPointAnnotation]()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
tableView.delegate = self
tableView.dataSource = self
mapView.delegate = self
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
tableView.allowsSelection = true
let currentLocation = locationManager.location
guard let latitude = currentLocation?.coordinate.latitude,
let longitude = currentLocation?.coordinate.longitude else {
alertPopup(title: "Enable Location Services", message: "Navigate to Settings >", buttonTitle: "Ok")
return
}
let initialLocation = CLLocation(latitude: latitude, longitude: longitude)
tabBarController?.tabBar.isHidden = true
centerMapOnLocation(location: initialLocation)
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = "Taxi"
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start(completionHandler: {(response, error) in
if error != nil {
print("Error occured in search: \(error!.localizedDescription)")
} else if response!.mapItems.count == 0 {
print("No Matches Found")
} else {
print("Matches Found")
}
for location in response!.mapItems {
print("Name = \(String(describing: item.name))")
print("Phone = \(String(describing: item.phoneNumber))")
self.matchingItems.append(item)
var pinAnnotationView = MKPinAnnotationView()
let annotation = MKPointAnnotation()
annotation.coordinate.longitude = location.placemark.coordinate.longitude
annotation.coordinate.latitude = location.placemark.coordinate.latitude
annotation.title? = location.name!
annotation.subtitle = location.phoneNumber!
self.annotationGroup.append(annotation)
self.mapView.addAnnotations(self.annotationGroup)
pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
self.mapView.addAnnotation(pinAnnotationView.annotation!)
}
self.tableView.reloadData()
print("Reloaded Table Data")
print(self.matchingItems)
self.mapView.showAnnotations(self.annotationGroup, animated: true)
})
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if !(annotation is MKUserLocation) {
let pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: String(annotation.hash))
let rightButton = UIButton(type: .contactAdd)
rightButton.tag = annotation.hash
pinView.animatesDrop = true
pinView.canShowCallout = true
pinView.rightCalloutAccessoryView = rightButton
return pinView
} else {
print("Returned nil")
return nil
}
}
}
The issue is the optional annotation.title? = location.name!. Remove the optional and it works.
for location in response!.mapItems {
print("Name = \(String(describing: location.name))")
print("Phone = \(String(describing: location.phoneNumber))")
self.matchingItems.append(location)
let annotation = MKPointAnnotation()
annotation.coordinate.longitude = location.placemark.coordinate.longitude
annotation.coordinate.latitude = location.placemark.coordinate.latitude
annotation.title = location.name! //This is the line to remove the optional annotation.title? from.
annotation.subtitle = location.phoneNumber!
self.annotationGroup.append(annotation)
self.mapView.addAnnotations(self.annotationGroup)
self.mapView.showAnnotations(self.annotationGroup, animated: true)
}
Maybe is too late but anyway
First of all, MKAnnotation is a protocol so I think that you must define a custom class for your objects, and this class must implement this protocol or in this case we can define an extension for MKMapItem implementing MKAnnotation protocol
extension MKMapItem : MKAnnotation
{
public var coordinate: CLLocationCoordinate2D {
return self.placemark.coordinate
}
// Title and subtitle for use by selection UI.
public var title: String? {
return self.name
}
public var subtitle: String? {
return self.phoneNumber
}
}
with this your code will be reduced to this
search.start(completionHandler: {(response, error) in
if error != nil {
print("Error occured in search: \(error!.localizedDescription)")
} else if response!.mapItems.count == 0 {
print("No Matches Found")
} else {
print("Matches Found")
}
for location in response!.mapItems {
print("Name = \(String(describing: item.name))")
print("Phone = \(String(describing: item.phoneNumber))")
self.matchingItems.append(item)
}
self.mapView.addAnnotations(self.matchingItems)
self.tableView.reloadData()
print("Reloaded Table Data")
print(self.matchingItems)
self.mapView.showAnnotations(self.matchingItems, animated: true)
})
Once you have this then your implementation of func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {} should work
Hope this helps
Related
I am coming to a problem where I try to generate different icons to show on the map view of places. But, I need some help from you guys. So far, I have hard-coded a pin to show on the map view. I also, have different pins in my assets, I want to show them by generating it on the mapview. How can I generate different icons to show on my map view from the API? Thanks for the help.
Here is my code:
import UIKit
import MapKit
import CoreLocation
class MapViewController: BaseViewController{
#IBOutlet weak var leadingConstraints: NSLayoutConstraint!
#IBOutlet weak var menuView: UIView!
#IBOutlet weak var mapView: MKMapView!
fileprivate let locationManager = CLLocationManager()
fileprivate var startedLoadingPOIs = false
fileprivate var places = [Place]()
fileprivate var arViewController: ARViewController!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
var nearMeIndexSelected = NearMeIndexTitle()
var menuShowing = false
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
locationManager.requestWhenInUseAuthorization()
//making shadow of our menu view
menuView.layer.shadowOpacity = 1
menuView.layer.shadowRadius = 7
}
#IBAction func showARController(_ sender: Any) {
arViewController = ARViewController()
arViewController.dataSource = self
arViewController.maxVisibleAnnotations = 30
arViewController.headingSmoothingFactor = 0.05
arViewController.setAnnotations(places)
self.navigationController!.pushViewController(arViewController, animated: true)
}
}
extension MapViewController: CLLocationManagerDelegate, MKMapViewDelegate {
// // Changing the Pin Color on the map.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
mapView.tintColor = #colorLiteral(red: 0.8823529412, green: 0.1647058824, blue: 0.1333333333, alpha: 1)
return nil
} else {
let annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
let pin = mapView.view(for: annotation) ?? MKAnnotationView(annotation: annotation, reuseIdentifier: nil)
pin.image = UIImage(named: "pins")
return pin
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.013, longitudeDelta: 0.013)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.region = region
if !startedLoadingPOIs {
DispatchQueue.main.async {
self.activityIndicator.startAnimating()
}
startedLoadingPOIs = true
let loader = PlacesLoader()
loader.loadPOIS(location: location, radius: 1500) { placesDict, error in
if let dict = placesDict {
guard let placesArray = dict.object(forKey: "results") as? [NSDictionary] else { return }
for placeDict in placesArray {
let latitude = placeDict.value(forKeyPath: "geometry.location.lat") as! CLLocationDegrees
let longitude = placeDict.value(forKeyPath: "geometry.location.lng") as! CLLocationDegrees
let reference = placeDict.object(forKey: "reference") as! String
let name = placeDict.object(forKey: "name") as! String
let address = placeDict.object(forKey: "vicinity") as! String
let location = CLLocation(latitude: latitude, longitude: longitude)
let place = Place(location: location, reference: reference, name: name, address: address)
self.places.append(place)
let annotation = PlaceAnnotation(location: place.location!.coordinate, title: place.placeName)
DispatchQueue.main.async {
self.mapView.addAnnotation(annotation)
}
}
DispatchQueue.main.async {
self.activityIndicator.stopAnimating()
self.mapView.isHidden = false
}
}
}
}
}
}
}
}
You can compare the coordinate of the annotation and specify custom pin for that Annotation.
if annotation.coordinate == "Your Custom Pin Coordinate" { //set custom pin }
Suppose I want to add a custom pin for my selected Place.
var selectedPlace: PlaceAnnotation
Inside your loop. suppose my selected place is "toronto"
if name == "toronto" { self.selectedPlace = annotation }
Then in ViewForAnnotation method
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.coordinate = selectedPlace.coordinate {
pin.image = UIImage(named: "YOUR SELECTED IMAGE")
}
}
See here my demo to create a custom pin view customPinAnnotationButton
Here is the method to draw annotation with image , I subclassed MKAnoationView
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation is MyAnnotation == false
{
return nil
}
let senderAnnotation = annotation as! MyAnnotation
let pinReusableIdentifier = senderAnnotation.pinColor.rawValue
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinReusableIdentifier)
if annotationView == nil
{
annotationView = MKAnnotationView(annotation: senderAnnotation, reuseIdentifier: pinReusableIdentifier)
annotationView!.canShowCallout = false
}
if senderAnnotation.pinColor == PinColor.Green
{
let pinImage = UIImage(named:"directMarker3.png")
annotationView!.image = pinImage
}
return annotationView
}
here to add an annotation
let blueLocation = CLLocationCoordinate2D(latitude:30.45454554, longitude: 29.646727)
let blueAnnotation = MyAnnotation(coordinate: blueLocation, title:"ghghhg",subtitle: "hgnhhghghg",pinColor: .Green ,uid:"hghg",type:"provider")
self.mymap.addAnnotation(blueAnnotation)
self.mymap.showAnnotations(self.mymap.annotations, animated: true)
1.Define subclass of MKPointAnnotation:
class MyPointAnnotation: MKPointAnnotation {
var imageName: String = ""
}
2.Set image name.
let annotation = MyPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "title"
annotation.subtitle = "subtitle"
annotation.imageName = "pin" // Set image name here
self.mapView.addAnnotation(annotation)
3.Load image in viewFor delegate method
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let reuseId = "image"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId)
if pinView == nil {
pinView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.canShowCallout = true
let annotation = annotation as! MyPointAnnotation
pinView?.image = UIImage(named: annotation.imageName)
let rightButton: AnyObject! = UIButton(type: UIButtonType.detailDisclosure)
pinView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
pinView?.annotation = annotation
}
return pinView
}
I have made an project and followed a tutorial on the internet. It shows annotations and has a custom callout view.I have added a label with a button feature which is shown in my screenshot. I now would like some help to get the feature to start maps app from my project. I have made comments in the project which shows what i have done and what i need help with
import UIKit
import MapKit
class ViewController: UIViewController, MKMapViewDelegate, UISearchBarDelegate {
#IBOutlet var mapView: MKMapView!
#IBOutlet var searchBarMap: UISearchBar!
var coordinates: [[Double]]!
var names:[String]!
var addresses:[String]!
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation
{
return nil
}
if annotation is MKPointAnnotation
{
return nil
}
var annotationView = self.mapView.dequeueReusableAnnotationView(withIdentifier: "Pin")
if annotationView == nil{
annotationView = AnnotationView(annotation: annotation, reuseIdentifier: "Pin")
annotationView?.canShowCallout = false
}else{
annotationView?.annotation = annotation
}
annotationView?.image = UIImage(named: "parking-sign")
return annotationView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
searchBarMap.delegate = self
coordinates = [[57.638486,18.299698],[57.636859,18.300468],[57.634807, 18.293389]]// Latitude,Longitude
names = ["Österport","Åhléns","Söderport/busstation"]
addresses = ["regler: p-skiva 2 timmar 9-18, oftast få platser lediga","stor med två våningar, övre plan oftast väl fylld och komplicerade parkeringsrutor","Nära om du vill till adelsgatan"]
self.mapView.delegate = self
for i in 0...2
{
let coordinate = coordinates[i]
let point = StarbucksAnnotation(coordinate: CLLocationCoordinate2D(latitude: coordinate[0] , longitude: coordinate[1] ))
point.name = names[i]
point.address = addresses[i]
self.mapView.addAnnotation(point)
}
let region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 57.630615, longitude: 18.303023), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
self.mapView.setRegion(region, animated: true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(_ mapView: MKMapView,
didSelect view: MKAnnotationView)
{
// 1
if view.annotation is MKUserLocation
{
return
}
if view.annotation is MKPointAnnotation
{
return
}
let starbucksAnnotation = view.annotation as! StarbucksAnnotation
let views = Bundle.main.loadNibNamed("CustomCalloutView", owner: nil, options: nil)
let calloutView = views?[0] as! CustomCalloutView
calloutView.starbucksName.text = starbucksAnnotation.name
calloutView.starbucksAddress.text = starbucksAnnotation.address
// declaring label as a button
let button = UIButton(frame: calloutView.directions.frame)
button.addTarget(self, action: #selector(ViewController.openInMaps(sender:)), for: .touchUpInside)
calloutView.addSubview(button)
calloutView.center = CGPoint(x: view.bounds.size.width / 2, y: -calloutView.bounds.size.height*0.52)
view.addSubview(calloutView)
mapView.setCenter((view.annotation?.coordinate)!, animated: true)
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
if view.isKind(of: AnnotationView.self)
{
for subview in view.subviews
{
subview.removeFromSuperview()
}
}
}
//I want to know what i should fill this function with to open maps and give me directions
func openInMaps(sender: UIButton)
{
//This is what i tested so far
let coordinates1 = coordinates
let point = StarbucksAnnotation(coordinate: CLLocationCoordinate2D(latitude: coordinates1[0] , longitude: coordinates1[1] ))
let regionDistance:CLLocationDistance = 10000
let regionSpan = MKCoordinateRegionMakeWithDistance(point, regionDistance, regionDistance)
let options = [
MKLaunchOptionsMapCenterKey: NSValue(mkCoordinate: regionSpan.center),
MKLaunchOptionsMapSpanKey: NSValue(mkCoordinateSpan: regionSpan.span)
]
let placemark = MKPlacemark(coordinate: point, addressDictionary: nil)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = "destination"
mapItem.openInMaps(launchOptions: options)
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
searchBarMap.resignFirstResponder()
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(searchBar.text!) { (placemarks:[CLPlacemark]?, error:Error?) in
if error == nil {
let placemark = placemarks?.first
let anno = MKPointAnnotation()
anno.coordinate = (placemark?.location?.coordinate)!
anno.title = self.searchBarMap.text!
self.mapView.addAnnotation(anno)
self.mapView.selectAnnotation(anno, animated: true)
}else{
print(error?.localizedDescription ?? "error")
}
}
}
}
I've been having trouble figuring out how to pass a custom variable in a map pin to another view controller in Swift. I know that passing the coordinates, title, and subtitle are available when you addAnnotation. I would like to try and pass a custom variable but hidden. Is there such a thing? Below I am getting the users location, mapping it, dropping a pin of a couple locations nearby with annotations which goes to another view controller and passes just the title and subtitle. Any insight is greatly appreciated.
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
var mappedCity = String()
var mappedState = String()
var manager = CLLocationManager()
var annotation:MKAnnotation!
var error:NSError!
var pointAnnotation:MKPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
var selectedAnnotation: MKPointAnnotation!
private var mapChangedFromUserInteraction = false
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
if CLLocationManager.locationServicesEnabled(){
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let userLocation:CLLocation = locations[0]
let latitude = userLocation.coordinate.latitude
let longitude = userLocation.coordinate.longitude
let latDelta:CLLocationDegrees = 0.05
let lonDelta:CLLocationDegrees = 0.05
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
let location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
let region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
self.mapView.setRegion(region, animated: true)
self.mapView.showsUserLocation = true
CLGeocoder().reverseGeocodeLocation(userLocation) { (placemarks, error) in
if (error != nil){
print(error)
}else {
if let p = placemarks?[0]{
let locality = p.locality ?? ""
let administrativeArea = p.administrativeArea ?? ""
self.mappedCity = String(locality)
self.mappedState = String(administrativeArea)
self.parseJSON("\(locality)", state: "\(administrativeArea)")
}
}
}
self.manager.stopUpdatingLocation()
}
func parseJSON(city: String, state: String){
let passedCity = city
let passedState = state
let escapedCity = passedCity.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let escapedState = passedState.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
let url = NSURL(string:"http://www.API.com/api.php?city=\(escapedCity)&stateAbv=\(escapedState)")!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { (items, response, error) -> Void in
if error != nil {
print(error)
}else {
if let items = items {
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(items, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if jsonResult.count > 0 {
if let datas = jsonResult["data"] as? NSArray{
for data in datas{
if let title = data["title"] as? String {
if let street = data["street"] as? String {
if let city = data["city"] as? String {
if let stateAbv = data["stateAbv"] as? String {
if let zip = data["zip"] as? String {
self.geoAddress("\(title)", street: "\(street)", city: "\(city)", state: "\(stateAbv)", zip: "\(zip)")
}
}
}
}
}
}
}
}
} catch{}
}
}
}
task.resume()
}
func geoAddress(title: String, street: String, city: String, state: String, zip: String){
let storeName = "\(title)"
let location = "\(street) \(city) \(state) \(zip)"
let geocoder = CLGeocoder();
geocoder.geocodeAddressString(location, completionHandler: {(placemarks: [CLPlacemark]?, error: NSError?) -> Void in
if (error != nil) {
print("Error \(error!)")
} else if let placemark = placemarks?[0] {
let coordinates:CLLocationCoordinate2D = placemark.location!.coordinate
let pointAnnotation:MKPointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = coordinates
pointAnnotation.title = storeName
pointAnnotation.subtitle = location
self.mapView.addAnnotation(pointAnnotation)
}
})
}
private func mapViewRegionDidChangeFromUserInteraction() -> Bool {
let view: UIView = self.mapView.subviews[0] as UIView
// Look through gesture recognizers to determine whether this region change is from user interaction
if let gestureRecognizers = view.gestureRecognizers {
for recognizer in gestureRecognizers {
if( recognizer.state == UIGestureRecognizerState.Began || recognizer.state == UIGestureRecognizerState.Ended ) {
return true
}
}
}
return false
}
func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
mapChangedFromUserInteraction = mapViewRegionDidChangeFromUserInteraction()
if (mapChangedFromUserInteraction) {
// user changed map region
}
}
func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
if (mapChangedFromUserInteraction) {
// user changed map region
let center = mapView.centerCoordinate
let mapLatitude = center.latitude
let mapLongitude = center.longitude
let locationmove = CLLocation(latitude: mapLatitude, longitude: mapLongitude)
CLGeocoder().reverseGeocodeLocation(locationmove) { (placemarks, error) in
if (error != nil){
print(error)
}else {
if let p = placemarks?[0]{
let locality = p.locality ?? ""
let administrativeArea = p.administrativeArea ?? ""
self.mappedCity = String(locality)
self.mappedState = String(administrativeArea)
self.parseJSON("\(locality)", state: "\(administrativeArea)")
}
}
}
}
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView
if pinView == nil {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.animatesDrop = false
pinView?.canShowCallout = true
pinView?.draggable = true
pinView?.pinTintColor = UIColor.greenColor()
let rightButton: AnyObject! = UIButton(type: UIButtonType.DetailDisclosure)
pinView?.rightCalloutAccessoryView = rightButton as? UIView
}
else {
pinView?.annotation = annotation
}
return pinView
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
selectedAnnotation = view.annotation as? MKPointAnnotation
performSegueWithIdentifier("Details", sender: self)
}
}
func mapView(mapView: MKMapView, annotationView view: MKAnnotationView, didChangeDragState newState: MKAnnotationViewDragState, fromOldState oldState: MKAnnotationViewDragState) {
if newState == MKAnnotationViewDragState.Ending {
let droppedAt = view.annotation?.coordinate
print(droppedAt)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "Details"){
let myDetails = segue.destinationViewController as! DetailViewController
myDetails.mytitle = selectedAnnotation.title
myDetails.mysubtitle = selectedAnnotation.subtitle
}
}
func updateSearchResultsForSearchController(searchController: UISearchController) {
}
}
Subclass the "MKPointAnnotation" class and add your custom property in it.
class MyAnnotation : MKPointAnnotation {
var customProperty : String?
}
And you can use MyAnnotation instead of MKPointAnnotation. Like following
let pointAnnotation:MyAnnotation = MyAnnotation()
pointAnnotation.coordinate = coordinates
pointAnnotation.title = storeName
pointAnnotation.subtitle = location
pointAnnotation.customProperty = "your value"
self.mapView.addAnnotation(pointAnnotation)
I'm trying to build an app for iOS 8 using Swift which uses a Parse.com database to display pins on a MapView. I've succeeded in loading all the pins on the map us PFGeoPoints, but I'm trying to add a disclosure button to each pin which will perform a segue to show extra info.
I've double checked my code but I'm missing something, does anyone notice problems?
import UIKit
import Foundation
import Parse
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
var userLocationParse = PFGeoPoint(latitude: 47.49, longitude: 19.06)
#IBOutlet weak var nmapview: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.requestWhenInUseAuthorization()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
let location = CLLocationCoordinate2D(latitude: 47.49, longitude: 19.06)
let span = MKCoordinateSpanMake(0.03, 0.03)
let region = MKCoordinateRegion(center: location, span: span)
nmapview.setRegion(region, animated: true)
nmapview.showsPointsOfInterest = false
nmapview.showsUserLocation = true
displayMarkers()
}
func displayMarkers() -> Void {
//GET PIN DATA HERE
var query = PFQuery(className: "Places")
query.whereKey("PlaceLocation", nearGeoPoint: userLocationParse)
query.limit = 30
let foundPlaces = query.findObjects()
//GETTING PFGEOLOCATIONS AND PUTTING THEM ON MAP AS ANNOTATIONS
//Loading pin details
var annotationQuery = PFQuery(className: "Places")
annotationQuery.whereKey("PlaceLocation", nearGeoPoint: userLocationParse)
annotationQuery.findObjectsInBackgroundWithBlock {
(posts, error) -> Void in
if error == nil {
// The find succeeded.
//println("Successful query for annotations")
let myPosts = posts as! [PFObject]
for post in myPosts {
let pinAnnotation = PinAnnotation()
let point = post["PlaceLocation"] as! PFGeoPoint
let pointName = post["PlaceName"] as! String
let pointDetails = post["PlaceDetails"] as! String
let thePinsLocation = CLLocationCoordinate2DMake(point.latitude, point.longitude)
pinAnnotation.setCoordinate(thePinsLocation)
pinAnnotation.title = pointName
pinAnnotation.subtitle = pointDetails
self.nmapview.addAnnotation(pinAnnotation)
}
} else {
// Log details of the failure
// println("Error: \(error)")
}
}
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if annotation is PinAnnotation {
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseID = "myPin"
var pinAnnotationView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseID) as? MKPinAnnotationView
if pinAnnotationView == nil {
pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseID)
pinAnnotationView!.pinColor = .Purple
pinAnnotationView!.canShowCallout = true
pinAnnotationView!.animatesDrop = true
pinAnnotationView!.rightCalloutAccessoryView = UIButton.buttonWithType(.DetailDisclosure) as! UIButton
} else {
pinAnnotationView!.annotation = annotation
}
return pinAnnotationView
}
return nil
}
func mapView(mapView: MKMapView!, annotationView view: MKAnnotationView!, calloutAccessoryControlTapped control: UIControl!) {
if control == view.rightCalloutAccessoryView{
performSegueWithIdentifier("infoViewController", sender: self)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Also I created a class for "PinAnnotation" for use with the pins. Not sure if redundant but some tutorials on the subject brought it up as necessary.
import UIKit
import MapKit
import UIKit
class PinAnnotation: NSObject, MKAnnotation {
private var coord: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 90.0, longitude: 0.0)
var coordinate: CLLocationCoordinate2D {
get {
return coord
}
}
var title: String = "North Pole"
var subtitle: String = "Santa's house"
func setCoordinate(newCoordinate: CLLocationCoordinate2D) {
self.coord = newCoordinate
}
}
On the basis of what you describe, it sounds like the delegate of the map view has not been set. You can set it in IB by going to the outlets inspector. You can set it programmatically with:
nmapview.delegate = self
I have MKAnnotations set up on a map, but I would like to change the color of the annotations for different scenario's. Is there a way to change the color of the annotation?
Here is my code below, how would I implement the color change?
override func viewDidAppear(animated: Bool) {
var annotationQuery = PFQuery(className: "Post")
currentLoc = PFGeoPoint(location: MapViewLocationManager.location)
//annotationQuery.whereKey("Location", nearGeoPoint: currentLoc, withinMiles: 10)
annotationQuery.whereKeyExists("Location")
annotationQuery.findObjectsInBackgroundWithBlock {
(points, error) -> Void in
if error == nil {
// The find succeeded.
println("Successful query for annotations")
// Do something with the found objects
let myPosts = points as! [PFObject]
for post in myPosts {
let point = post["Location"] as! PFGeoPoint
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude)
annotation.title = post["title"] as! String!
annotation.subtitle = post["username"] as! String!
self.mapView.addAnnotation(annotation)
}
} else {
// Log details of the failure
println("Error: \(error)")
}
}
You can use custom images for annotation view or use predefined MKPinAnnotationView with pinColor. But pinColors limited to Red, Green and Purple.
Some example:
import UIKit
import MapKit
class Annotation: NSObject, MKAnnotation
{
var coordinate: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: 0.0, longitude: 0.0)
var custom_image: Bool = true
var color: MKPinAnnotationColor = MKPinAnnotationColor.Purple
}
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self;
let annotation = Annotation.new()
mapView.addAnnotation(annotation)
let annotation2 = Annotation.new()
annotation2.coordinate = CLLocationCoordinate2D(latitude: 0.0, longitude: 1.0)
annotation2.custom_image = false
mapView.addAnnotation(annotation2)
let annotation3 = Annotation.new()
annotation3.coordinate = CLLocationCoordinate2D(latitude: 1.0, longitude: 0.0)
annotation3.custom_image = false
annotation3.color = MKPinAnnotationColor.Green
mapView.addAnnotation(annotation3)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if (annotation is MKUserLocation) {
return nil
}
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
if let anAnnotation = annotation as? Annotation {
if anAnnotation.custom_image {
let reuseId = "custom_image"
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.image = UIImage(named:"custom_image")
}
else {
let reuseId = "pin"
let pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView.pinColor = anAnnotation.color
anView = pinView
}
}
anView.canShowCallout = false
}
else {
anView.annotation = annotation
}
return anView
}
}
Update:
Set delegate for mapView in viewDidLoad
You have to set it in the viewForAnnotation method.
In this tutorial is quite well explained how doing it.