I am trying to create a custom Callout but for some reason the default point image does not change.
I am fetching the data for the points using Parse.
I have created a custom class (customAnnotationView) which inherits from MKAnnotationView and I am also overriding the mapView func.
The view controller is the following:
import UIKit
import Parse
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate{
#IBOutlet weak var map: MKMapView!
var locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate=self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
var annotationQuery = PFQuery(className: "Doc")
annotationQuery.findObjectsInBackgroundWithBlock {
(Doc, error) -> Void in
if error == nil {
// The find succeeded.
print("Successful query for annotations")
let docs = Doc
for doctor in docs! {
let point = doctor["location"] as! PFGeoPoint
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude)
annotation.title = String(doctor["name"])+String(doctor["surname"])
annotation.subtitle = String(doctor["address"])
print(doctor)
self.map.addAnnotation(annotation)
}
} else {
// Log details of the failure
print("Error: \(error)")
}
}
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var userLocation:CLLocation = locations[0]
var userLangtitude = userLocation.coordinate.latitude
var userLongtitude = userLocation.coordinate.longitude
// Initialize Map
var latidude:CLLocationDegrees = userLangtitude
var longtitude:CLLocationDegrees = userLongtitude
var latDelta:CLLocationDegrees = 2
var lonDelta:CLLocationDegrees = 2
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
var location: CLLocationCoordinate2D = CLLocationCoordinate2DMake(latidude,longtitude)
var region:MKCoordinateRegion = MKCoordinateRegionMake(location, span)
self.map.setRegion(region, animated: true)
self.map.showsUserLocation = true;
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
// If annotation is user current location do not change the annotation
if annotation.isKindOfClass(MKUserLocation) {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationViewWithIdentifier("customAnnotationView")
if annotationView == nil{
annotationView = customAnnotationView(annotation: annotation, reuseIdentifier: "customAnnotationView")
annotationView?.canShowCallout = true
}
else{
annotationView?.annotation = annotation
}
return annotationView
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The customAnnotationView:
import UIKit
import MapKit
import CoreLocation
import Parse
class customAnnotationView: MKAnnotationView {
override init(frame: CGRect) {
super.init(frame: frame)
}
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
var frame = self.frame
frame.size = CGSizeMake(80,80)
self.frame = frame
self.backgroundColor = UIColor.clearColor()
self.centerOffset = CGPointMake(-5, -5)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func drawRect(rect: CGRect) {
UIImage(named: "icon.png")?.drawInRect(CGRectMake(30,30, 30, 30))
}
}
Any help would be highly appreciated !
Thanks !
Related
I am struggling with my iOS code. The overall goal of the project is to show the pins on the map, I've tried multiple ways to make the pins appear on the map such as the below, I am not sure what I am doing wrong. The code runs fine, the pins are not appearing. I've made changes to my functions but seem to get the same error. Any insight on what I may be missing?
"import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var logout: UIBarButtonItem!
#IBOutlet weak var refresh: UIBarButtonItem!
#IBOutlet weak var pinDrop: UIBarButtonItem!
var studentLocation = [StudentInformation]()
var annotations = [MKPointAnnotation]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewDidAppear (_ animated: Bool) {
super.viewDidAppear(true)
getStudentPins()
}
#IBAction func refresh(_ sender: UIBarButtonItem) {
getStudentPins ()
}
#IBAction func logout(_ sender: UIBarButtonItem) {
self.activityIndicator.startAnimating()
UdacityClient.logout {
DispatchQueue.main.async {
self.dismiss(animated: true, completion: nil)
self.activityIndicator.stopAnimating()
}
}
}
#IBAction func addLocation(_ sender: Any) {
performSegue(withIdentifier: "addLocation", sender: nil)
}
func getStudentPins () {
self.activityIndicator.startAnimating()
UdacityClient.getStudentLocations() { locations, error in
self.mapView.removeAnnotations(self.annotations)
self.annotations.removeAll()
self.studentLocation = locations ?? []
for dictionary in locations ?? [] {
let latitude = CLLocationDegrees(dictionary.latitude ?? 0.0)
let longitude = CLLocationDegrees(dictionary.longitude ?? 0.0)
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let firstName = dictionary.firstName
let lastName = dictionary.lastName
let mediaURL = dictionary.mediaURL
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = "\(firstName) \(lastName)"
annotation.subtitle = mediaURL
self.annotations.append(annotation)
}
DispatchQueue.main.async {
self.mapView.addAnnotations(self.annotations)
self.activityIndicator.stopAnimating()
}
}
}
private func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotation? {
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 = .green
pinView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
else {
pinView!.annotation = annotation
}
return pinView?.annotation
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
if control == view.rightCalloutAccessoryView {
if let toOpen = view.annotation?.subtitle {
openLink(toOpen ?? "")
}
}
}
}"
They're called annotations in the MapKit and you should instantiate them like so:
let annotation = MKPointAnnotation()
then in the viewDidLoad() method just set the coordinates and add them to the map like:
annotation.coordinate = CLLocationCoordinate2D(latitude: 11.12, longitude: 12.11)
mapView.addAnnotation(annotation)
The numbers are your coordinates. Try by adding sample coordinates on the map first and then add all coordinates which you are getting from the client.
you can also add more information using:
annotation.title = "Your text here"
//You can also add a subtitle that displays under the annotation such as
annotation.subtitle = "One day I'll go here..."
I've tried to add title and subtitle to my annotations, but all my attempts had failed. Please help me!
import UIKit
import MapKit
class MyPointAnnotation : MKPointAnnotation{
var pinTintColor: UIColor?
}
class MapViewController: UIViewController, MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self;
let hello = MyPointAnnotation()
hello.coordinate = CLLocationCoordinate2D(latitude: 46.771210, longitude: 23.623635)
hello.title = "tile"
hello.subtitle = "subtitle"
hello.pinTintColor = .blue
let hellox = MyPointAnnotation()
hellox.coordinate = CLLocationCoordinate2D(latitude: 44.426767, longitude: 26.102538)
hellox.pinTintColor = .blue
mapView.addAnnotation(hello)
mapView.addAnnotation(hellox)
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
} else {
annotationView?.annotation = annotation
}
if let annotation = annotation as? MyPointAnnotation {
if #available(iOS 9.0, *) {
annotationView?.pinTintColor = annotation.pinTintColor
} else {
// Fallback on earlier versions
}
}
return annotationView
}
}
My problem solved with below code.In swift 4
#IBOutlet weak var map: MKMapView!
override func viewDidLoad() {
let location = CLLocationCoordinate2D(latitude: 12.956360, longitude: 77.716615)
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: 12.956360, longitude: 77.716615)
map.setRegion(MKCoordinateRegionMakeWithDistance(location, 1500, 1500), animated: true)
annotation.title = "your title"
annotation.subtitle = "your subtitle"
map.addAnnotation(annotation);
}
You need set canShowCallout true to show title and subtitle.
Try the following code.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "myAnnotation") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "myAnnotation")
annotationView?.canShowCallout = true
} else {
annotationView?.annotation = annotation
}
if let annotation = annotation as? MyPointAnnotation {
if #available(iOS 9.0, *) {
annotationView?.pinTintColor = annotation.pinTintColor
} else {
// Fallback on earlier versions
}
}
return annotationView
}
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
Could someone let me know why my labels are not being displayed. I am running a loop thru an array with coordinates in it. Its displaying my 3 pins with 1 being green, two blue which is what i want but my labels are not being displayed, any ideas?
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet weak var maps: MKMapView!
var locationManager = CLLocationManager()
var flag = true
override func viewDidLoad() {
super.viewDidLoad()
let CoordinatesArray = ["blah,-blah, 11:45","blah, -blah,00:00", "blah,-blah, 12:45"];
self.maps.delegate = self
sendPoints(CoordinatesArray);
}
func sendPoints(array:[String]){
let latDelta:CLLocationDegrees = 0.015 //difference of lats from one side of screen to another
let longDelta:CLLocationDegrees = 0.015 //difference of lats from one side of screen to another
let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
for (var i=0;i<array.count;i++){
var separateComma = array[i].componentsSeparatedByString(",")
var location:CLLocationCoordinate2D
if(flag){
location = CLLocationCoordinate2DMake(separateComma[0].doubleValue,separateComma[1].doubleValue)
let length:CLLocationDistance = 200
let cir:MKCircle = MKCircle(centerCoordinate: location, radius: length)
maps.addOverlay(cir)
}else{
location = CLLocationCoordinate2DMake(separateComma[0].doubleValue,separateComma[1].doubleValue)
}
let point = MKPointAnnotation()
point.title = "Home"
point.subtitle = "time for home"
point.coordinate = location
maps.addAnnotation(point)
maps.selectAnnotation(point, animated: true)
maps.setRegion(MKCoordinateRegionMake(point.coordinate, MKCoordinateSpanMake(latDelta,longDelta)), animated: true)
}
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
let overlayRenderer : MKCircleRenderer = MKCircleRenderer(overlay: overlay);
overlayRenderer.lineWidth = 150
overlayRenderer.fillColor = UIColor.blueColor()
overlayRenderer.alpha = 0.15
return overlayRenderer
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
if (annotation.isKindOfClass(MKUserLocation)){
return nil
}
var myPin = mapView.dequeueReusableAnnotationViewWithIdentifier("MyIdentifier") as? MKPinAnnotationView
if myPin != nil {
return myPin
}
myPin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "MyIdentifier")
if(flag){
myPin?.pinTintColor = UIColor.greenColor()
}else{
myPin?.pinTintColor = UIColor.blueColor()
}
flag = false;
return myPin
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension String {
var doubleValue: Double {
return (self as NSString).doubleValue
}
}
You have to set canShowCallout property.
myPin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "MyIdentifier")
myPin?.canShowCallout = true
In my case I was delegating the map before being called.
So... that'd be the mistake I was facing.
self.m_map = MKMapView()
self.m_map?.delegate = self
Hope it helps somebody :-)