I'm trying to draw an MKPolyline using GPS coordinates and show the user's current location along that polyline. However, I do not want to show the base map layer, I'd just like a transparent background.
Is this possible? How can I do this? Swizzling? Third-party? Something else? Everything is in play.
Thanks
Rather than diving through the map view's subviews, I would recommend trying to implement your own MKTileOverlay class with canReplaceMapContent set to YES and fully transparent tiles. My guess is that the map view would simply draw its backgroundColor behind the tiles, and you could set that to clearColor.
There is a pretty good NSHipster article that can get you started.
use empty MKTileOverlay
lazy var mapView: MKMapView = { () -> MKMapView in
let view = MKMapView()
view.mapType = .satellite
view.delegate = self
let overlay = MKTileOverlay()
overlay.canReplaceMapContent = true
view.addOverlay(overlay)
return view
}()
override func loadView() {
self.view = mapView
}
and render
extension ContentViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
return MKOverlayRenderer(overlay: overlay)
}
}
You can do something like,
NSLog(#"mapview background,%#",myMapView.subviews); //here myMapView is MKMapView object!
for (int i = 0; i < myMapView.subviews.count; i++) {
if ([[myMapView.subviews objectAtIndex:i] isKindOfClass:[UIView class]]) {
UIView *tempView = (UIView*)[myMapView.subviews objectAtIndex:i];
tempView.alpha = 0;
}
}
Hope this will help :)
Related
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()
}
}
}
}
}
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
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?
I created a MapViewController class with a scene that has an MKMapView in the storyboard. The MKMapView has an outlet mapView to MapViewController, and the delegate is set to MapViewController in the storyboard. The MapViewController is a MKMapViewDelegate, and I implemented the optional delegate method mapView:rendererForOverlay, and some other ones.
In viewDidAppear, I call addMapOverlay, defined as:
func addMapOverlay() {
let overlayRect = MKMapRectMake(
44.97077, -93.2864435,
0.1, 0.1)
let coordinate = CLLocationCoordinate2DMake(44.97087, -93.2865435)
let overlay = MapOverlay(coordinate: coordinate, rect: overlayRect)
print("adding overlay with level...")
mapView.delegate = self;
mapView.addOverlay(overlay, level: .AboveRoads)
}
Even though the delegate is set in the storyboard, I tried to see if would help if I set it here as well. (No difference.)
I implement the delegate methods as such:
extension MapViewController: MKMapViewDelegate {
func mapViewWillStartRenderingMap(mapView: MKMapView) {
print("mapViewWillStartRenderingMap...")
}
func mapView(mapView: MKMapView, didAddOverlayViews overlayViews: [AnyObject]) {
print("mapView:didAddOverlayViews...")
}
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay)
-> MKOverlayRenderer {
print("mapView:rendererForOverlay")
if overlay is MapOverlay {
let image = UIImage(named: "main_map_overlay")
let overlayView = MapOverlayView(overlay: overlay, overlayImage: image!)
return overlayView
}
return MKPolylineRenderer()
}
}
I am supposed to see the UIImage in the mapView with the coordinates, but I don't. The problem is that mapView:rendererForOverlay never gets called. What am missing? My only output is:
adding overlay with level...
mapViewWillStartRenderingMap...
Well I figured out the answer to my problem. Turns out, CLLocationCoordinate2DMake(44.97087, -93.2865435) is not the correct way to make the coordinate in this case. It's wrong one way or another, and as a result, adding the overlay silently fails without any helpful information. I did the following and it's fine now:
let parkTopLeft = MKMapPointForCoordinate(CLLocationCoordinate2D(
latitude: 44.972189,
longitude: -93.287307))
let parkBottomRight = MKMapPointForCoordinate(CLLocationCoordinate2D(
latitude: 44.967810,
longitude: -93.28186))
let overlayRect = MKMapRectMake(
parkTopLeft.x,
parkTopLeft.y,
parkBottomRight.x - parkTopLeft.x,
parkBottomRight.y - parkTopLeft.y)
let coordinate = CLLocationCoordinate2DMake(
overlayRect.origin.x + overlayRect.size.width / 2,
overlayRect.origin.y + overlayRect.size.height / 2)
let overlay = MapOverlay(coordinate: coordinate, rect: overlayRect)
mapView.addOverlay(overlay, level: .AboveRoads)
I've currently developing an iOS application with a dark theme, and would really like to make the displayed Apple Map also in dark colors.
I've Googled the topic and tried to change the properties of the component through the Storyboard. But, I couldn't find anything.
Is this even possible? Can I change the colors of the Apple Map component... or even just invert the color to make it look dark?
Thank you.
It's a little clumsy and limited, but you could put a UIView with black background color and alpha less than 1.0 (e.g. 0.3) over the map (make sure to turn off user interaction with this view so gestures get passed through to the map) and that would dim the whole thing. A little kludgy and you lose contrast, but it might be worth trying.
You can't do this without using third-party providers like MapBox, at least on iOS 6 and below.
You can create a subclass of MKTileOverlay like
class DarkModeMapOverlay: MKTileOverlay {
init() {
super.init(urlTemplate: nil)
canReplaceMapContent = true
}
override func url(forTilePath path: MKTileOverlayPath) -> URL {
let tileUrl = "https://a.basemaps.cartocdn.com/dark_all/\(path.z)/\(path.x)/\(path.y).png"
return URL(string: tileUrl)!
}
}
and then set the overlay on your MKMapView with
class MapViewController: UIViewController, MKMapViewDelegate {
private var tileRenderer: MKTileOverlayRenderer?
private var mapView: MKMapView {
return view as! MKMapView
}
private func configureTileRenderer() {
let darkModeOverlay = DarkModeMapOverlay()
tileRenderer = MKTileOverlayRenderer(tileOverlay: darkModeOverlay)
mapView.addOverlay(darkModeOverlay, level: .aboveLabels)
}
override func loadView() {
view = MKMapView(frame: .zero)
}
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
configureTileRenderer()
}
// MARK: MKMapViewDelegate
func mapView(_: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
guard let renderer = self.tileRenderer else {
return MKOverlayRenderer(overlay: overlay)
}
return renderer
}
}