MapKit pin title not visible when placed on overlay? - ios

When I place a pin on top of an overlay, the title of the pin appears to be obscured. When the pin is placed over a point that is not on top of the overlay, the title appears below the pin.
The string lastCoordName is the title of the pin; this string is passed from the previous view controller to the current one (shown below).
Here are some images to describe what I'm talking about...
Above image shows the title right below the pin ("360") when the pin is off the overlay.
But the title goes away when the pin is moved to the correct position on top of the overlay.
The title is still shown when the polyline is drawn horizontally and terminates off the overlay, so the problem isn't that the line is covering the title.
Here is the custom pin class:
class CustomPin : NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var title: String?
init(coordinate: CLLocationCoordinate2D, title: String) {
self.coordinate = coordinate
self.title = title
super.init()
}
}
The relevant part of viewDidLoad()
let pin = CustomPin(coordinate: endPoint, title: lastCoordName)//uses lastCoordName from previous vc (insead of looking up name of last node given coord)
mapView.addAnnotation(pin)
mapView.selectAnnotation(pin, animated: true)
And the mapViewController extension
extension mapViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if overlay is SchoolMapOverlay {
return SchoolMapOverlayView(overlay: overlay, overlayImage: #imageLiteral(resourceName: "GBSF1"))
} else if overlay is MKPolyline {
let lineView = MKPolylineRenderer(overlay: overlay)
lineView.strokeColor = UIColor(red:0.2, green:0.48, blue:1.00, alpha:1.0)
lineView.lineWidth = 10.0
return lineView
}
return MKOverlayRenderer()
}
func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
pin.canShowCallout = true
return pin
}
}
Is the overlay covering the pin title? If so, why are the pin and its title not on the same level (above the overlay)?

You can set the layer the overlay is drawn on with
mapView.add(tileOverlay, level: .aboveRoads)
This will draw it above roads but below labels. Problem is, it's below all labels; so depending on what you're showing in your map, you're gonna have to play with labels a bit.
MKOverlayLevel Documentation

Related

how to make a button of free shape?

I want to create map app. For example, the are different areas in Japan. All of them are different shape, not square. You push on it and it gonna change the color. It is possible to create the button of unusual form (the form of region on the map) in Xcode?
Drawing
In order to draw the custom shape on the map, use your custom subclass of MKPolygon where you'll define your selected state color, and selected flag.
internal final class MyPolygon: MKPolygon {
internal var selected = false
internal var selectedColor = UIColor.red.withAlphaComponent(0.6)
...
}
In order to draw a custom color for your custom MKPolygon, create your subclass of MKPolygonRenderer and use custom drawing to fill the polygon with the selectedColor when it has selected == true.
When those two implemented, add your overlay onto MKMapView
mapView.addOverlay(polygon)
Assign the delegate to your mapView, and return your renderer for the polygon from the delegate's method:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let polygon = overlay as? MyPolygon {
return MyPolygonRenderer(polygon: polygon)
}
return MKOverlayRenderer(overlay: overlay)
}
Catching user input
Add UITapGestureRecognizer onto your mapView:
func viewDidLoad() {
super.viewDidLoad()
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(mapViewDidTap(_:)))
mapView.addGestureRecognizer(tapRecognizer)
...
}
func mapViewDidTap(_ tapRecognizer: UITapGestureRecognizer) {
if tapRecognizer.state == .recognized {
// Get map coordinate from touch point
let touchCoord = tapRecognizer.location(in: mapView)
let coord = mapView.convert(touchCoord, toCoordinateFrom: mapView)
let coordRect = MKMapRect(origin: coord, size: MKMapSize())
// for every overlay ...
for overlay in mapView.overlays {
// .. if MyPolygon ...
if let polygon = overlay as? MyPolygon {
let selected = polygon.intersects(coordRect)
if polygon.selected != selected {
polygon.selected = selected
mapView.renderer(for: polygon)?.setNeedsDisplay()
}
}
}
}
}

MKOverlay not appearing until MKMapView is zoomed or moved?

So I got an MKCirlce (MKOverlay), and I've added it like this:
self.current_location_overlay = MKCircle(center: self.current_location!, radius: 200)
self.mk_map_view.add(self.current_location_overlay!)
I've set the map delegate and added this MKMapViewDelegate method:
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let overlay = overlay as? MKCircle {
let circle_renderer = MKCircleRenderer(circle: overlay)
circle_renderer.fillColor = UIColor.red.withAlphaComponent(0.2)
circle_renderer.setNeedsDisplay()
return circle_renderer
} else {
return MKOverlayRenderer(overlay: overlay)
}
}
Tho the overlay is not appearing at start, only after panning or zooming the map it appears.
Seems like others have had this problem, tho no solution on this post seems to help.
Dose anyone know how to solve this?

Creating a custom map annotation callout with an image, accessing data from an array

I have a parsed a set of data from a JSON file (see example below):
{
"locations": [
{
"id": "0001",
"name": "Helensburgh Tunnels",
"type": "Tunnels, Beach, Views",
"location": "Helensburgh, South Coast",
"image": "Helensburgh-Tunnels.jpg",
"activity": "Walking, photography, tunnelling",
"isVisited": false,
"latitude": -34.178985,
"longitude": 150.992867
}
]
}
I am able to correctly read all of this data into a TableView (everything works correctly), however, I would also like to display all of the locations within the JSON file as annotations on a MapView. So far, everything appears correctly except for a preview image on the left side of the callout box (all the annotation pins appear, when clicked they show a callout with a title and subtitle, but no image).
What do I need to resolve so that I can show this image? I am able to achieve it in other sections of my app, where only one location is shown, however, I cannot seem to figure out how to add images to all the annotation callouts within this view.
This is the code I am using to populate the annotations in my NearMeMapViewController:
import UIKit
import MapKit
import CoreLocation
class NearMeMapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
#IBOutlet var nearMeMap: MKMapView!
let locationManager = CLLocationManager()
var locations = [Location]()
var location:Location!
override func viewDidLoad() {
super.viewDidLoad()
// parse json
if let locationJson = readLocation(){
if let locationArray = locationJson["locations"] as? [[String:Any]]{
for location in locationArray{
locations.append(Location.init(locationInfo: location))
}
print(locations.count)
}
}
// end parse json
nearMeMap.delegate = self
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.nearMeMap.showsUserLocation = true
// Show annotation
for location in locations {
let annotation = MKPointAnnotation()
annotation.title = location.name
annotation.subtitle = location.type
annotation.coordinate = CLLocationCoordinate2D(latitude: location.latitude, longitude: location.longitude)
nearMeMap.addAnnotation(annotation)
}
}
// Start test - Custom annotation callout with image
func nearMeMap (_ nearMeMap: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let identifier = "MyPin"
if annotation.isKind(of: MKUserLocation.self) {
return nil
}
// Reuse the annotation if possible
var annotationView:MKPinAnnotationView? = nearMeMap.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = true
}
let leftIconView = UIImageView(frame: CGRect.init(x: 0, y: 0, width: 53, height: 53))
leftIconView.image = UIImage(named: location.image)
annotationView?.leftCalloutAccessoryView = leftIconView
return annotationView
}
// End test
I would really appreciate any help! I'm a student and I'm just learning, so sorry if I've used any incorrect terminology or there are noob mistakes.
Swift 3.0
On above code the MKMapViewDelegate should be
func mapView (_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{...}
and not
func nearMeMap (_ nearMeMap: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {...}
Add var currentIndex = 0 as global declaration to identify the current image index. else, you aware to use id from JSON response.
i.e., Global Scope becomes like below code,
let locationManager = CLLocationManager()
var locations = [Location]()
//test variable? implemented to resolve issue with images in custom callout
var location:Location!
var currentIndex = 0
override func viewDidLoad() {...}
Then, the leftIconView will becomes,
let leftIconView = UIImageView(frame: CGRect.init(x: 0, y: 0, width: 53, height: 53))
var images = [String]()
for location in locations{
images.append(location.image)
}
leftIconView.image = UIImage(named: images[currentIndex])
self.currentIndex = self.currentIndex+1 //0,1,2,3...N.
Output:-
when you want to set custom image for annotation view, prefer MKAnnotationView instead of an MKPinAnnotationView. You can set your custom image as annotation by setting it as Image attribute of MKAnnotationView . Check here for more info on MKAnnotationView .
As MKPinAnnotationView is a subclass of MKAnnotationView, it has an image property but it generally (usually when you don't expect it) ignores it and displays its built-in pin image instead.
So it's best to just use a plain MKAnnotationView.
See this answer for more details.You can use MKAnnotationView as explained here.
----- EDIT -----
hide your basic Callout to show your custom image.
annotationView?.canShowCallout = false
Refer this ans to customize CallOut images. Check this link for the detailed tutorial.
Hope it helps. Happy Coding!!

MapView custom pins not displaying details when clicked

I am trying to display the results of an array of places' names on a MapView. Using regular pins is not an issue: they display normally, and when clicked, the detail cell appears with the name of the place.
for var t = 0;t<self.parties2.count;++t {
var placeLatitude:CLLocationDegrees = self.parties2[t].latitude
var placeLongitude:CLLocationDegrees = self.parties2[t].longitude
var placeLocation = CLLocationCoordinate2DMake(placeLatitude, placeLongitude)
var annotation = MKPointAnnotation()
annotation.coordinate = placeLocation
annotation.title = self.parties2[t].name
self.mapView.addAnnotation(annotation)
}
Then I changed the aspect of the pin with this function:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
let identifier = "pin"
var view: MKAnnotationView
view = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
var image = UIImage(named: "symbol.png")
image = self.imageResize(image!, sizeChange: CGSizeMake(20, 20))
view.image = image
return view
}
(I resize the pin image with the imageResize function)
This function succeeds in changing the aspect of the pin: it indeed becomes the desired image. However, clicking these images does not display the details anymore.
OK I just needed to add pinView.canShowCallout = true to my code, I assumed it was true by default since it is not necessary with the original pin.

How to overlay an image on a IOS map using swift

I am trying to find out how to overlay an image on a IOS map using SWIFT. I have created the following code that overlays a green circle on a map using map kit. I want to replace the green circle with the rectangular image tOver.png 500,500 I am new to iOS development and to swift. So far I can not find a swift example or good resource.
//
// ViewController.swift
// mapoverlaytest
//
import UIKit
import MapKit
class ViewController: UIViewController,MKMapViewDelegate {
#IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
self.mapView.delegate = self;
let location = CLLocationCoordinate2D(
latitude: 51.50007773,
longitude: -0.1246402
)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location, span: span)
mapView.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.setCoordinate(location)
annotation.title = "Big Ben"
annotation.subtitle = "London"
var overlay = MKCircle (centerCoordinate: location, radius: 500)
mapView.addOverlay(overlay)
mapView.addAnnotation(annotation)
}
func mapView(
mapView: MKMapView!, rendererForOverlay
overlay: MKOverlay!) -> MKOverlayRenderer! {
if (overlay.isKindOfClass(MKCircle))
{
var circleRenderer = MKCircleRenderer(overlay: overlay)
circleRenderer.strokeColor = UIColor.greenColor()
circleRenderer.fillColor = UIColor(
red: 0,
green: 1.0,
blue: 0,
alpha: 0.5)
return circleRenderer
}
return nil
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
As Totem explained, it would be simpler to use an image annotation instead of an overlay if that works for your purposes. It may not work depending on what you want to use this image for, however. The main difference between map overlays and map annotations, is annotations stay the same size when you zoom the map (like a pin), and overlays change with the size of the map (like marking a building). If you want your image to zoom with the map, it gets a little more complicated.
You will want to create a new MKOverlayRenderer subclass to draw your image. You have to draw the image into the image context yourself by subclassing the drawMapRect(mapRect, zoomScale, inContext) function. After you make this subclass, you can just substitute in your custom subclass in place of the MKCircleRenderer, and you should be good to go.
There is a very good write up on Raywenderlich.com, which you should definitely check out. It should walk you through everything you need to know.
Instead of rendererForOverlay, you should implement
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView!
Inside there, build your MKAnnotationView and set its image property before returning it out. Check out https://developer.apple.com/LIBRARY/ios/documentation/MapKit/Reference/MKAnnotationView_Class/index.html for more info on the MKAnnotationView class.

Resources