13

I am looking for a way to implement a custom map style in iOS 7, just like you can do with Google Maps. I have found some posts saying that this is not possible with MapKit, but they are all posted a while back. To clarify, by style I am talking about custom colors and preferably also fonts. Example of custom Google Map style below.

enter image description here
(source: servendesign.com)

I would really prefer using MapKit for performance reasons, but if it is not supported I am open to using other frameworks as well. The ones that I have seen are MapBox and Cloudmade, and of course the Google Maps SDK.

Is there a way of doing it with MapKit? If not, what is the best way to go?

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
Daniel Larsson
  • 6,278
  • 5
  • 44
  • 82

3 Answers3

19

MKMapView also offers the possibility to use custom tile overlays. Openstreetmap has a great list of tile servers you could use to get a custom map. Of course there is always the possibility to create your own tile overlay set. The process is described in the Openstreetmap wiki here.

A possible implementation in Swift could look like this:

1. Import MapKit

import MapKit

2. Add overlays to map

let overlayPath = self.mapViewModel.overlayURL
let overlay = MKTileOverlay(URLTemplate: overlayPath)
overlay.canReplaceMapContent = true
self.mapView.addOverlay(overlay)

3. Conform to MKMapViewDelegate

class ViewController: UIViewController, MKMapViewDelegate { ... }

4. Implement delegate method to use the correct renderer to display the tiles

func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
    guard let tileOverlay = overlay as? MKTileOverlay else {
        return MKOverlayRenderer(overlay: overlay)
    }
    return MKTileOverlayRenderer(tileOverlay: tileOverlay)
}

In the above example overlayURL is taken from the tile server list found on openstreetmap: OpenstreetMap Tile Servers.

For example if you would like to use the stamen map (which has a watercolor style) your url would look like:

let overlayURL = "http://tile.stamen.com/watercolor/{z}/{x}/{y}.jpg"

If you are searching for a dark-mode map you probably go best with Carto Dark: http://a.basemaps.cartocdn.com/dark_all/${z}/${x}/${y}.png.

See that the above URLs has no SSL support (HTTP). Therefore you will need to allow insecure HTTP requests to this specific URL by adding the App Transport Security Settings in your Info.plist. For further information have a look at this link.

Community
  • 1
  • 1
dehlen
  • 7,325
  • 4
  • 43
  • 71
  • Hello @dehlen, thanks for this helpful answer. Quick Q: where in my MapViewController do I put this code (particularly the block in point 2, as I'm getting red error msgs wherever I place it. – Dimitri T Aug 24 '16 at 05:03
  • @DimitriT you can put it in your viewDidLoad func for example. – dehlen Oct 21 '16 at 06:28
  • thank you Dehlen. If you may be so kind as to explain for a beginner like me - what code am I supposed to add, to get the mapViewModel class as I am getting the error 'use of unresolved identifier MapViewModel'. I presume I add a new swift file, wondering how to add this. Just got the hang of the maps and annotations- Any tips on this class would be a great help. – Dimitri T Oct 21 '16 at 13:34
  • @DimitriT Its just a class where I stored a constant pointing to the right tileserver URL. You can replace `let overlayPath = self.mapViewModel.overlayURL` with `let overlayPath = "http://tile.stamen.com/watercolor/{z}/{x}/{y}.jpg" `like I stated in the answer above. – dehlen Oct 21 '16 at 13:51
  • How can I get those variables (x, y, z)? – Francisco Castro Jan 11 '17 at 16:52
  • 1
    You don't need them. They are aubstituted by the mapview itself. Just keep the placeholders in the string. – dehlen Jan 11 '17 at 16:59
  • You would need to download the tile data and bundle it with your app. The template url could then be a file url rather than a http url. – dehlen Jan 11 '17 at 21:36
  • Example: `let bundleURL = Bundle.main.bundleURL.absoluteString let overlayURL = bundleURL.appending("/tile-data/{z}/{x}/{y}.png") self.tileOverlay = MKTileOverlay(URLTemplate: overlayURL)` where your downloaded tile data is in a folder called tile-data and this folder is added to your app bundle. – dehlen Jan 11 '17 at 21:51
  • This doesn't answer the question how to change the background color of MKMapView. Even you used customized tile, but the map background is still not changed. For example, I only show a map for a country through customized tiles. But the area outside of this country will be in map original background. – Bagusflyer Jul 30 '19 at 03:38
9

MKMapView does not expose the properties you're interested in customizing. The Google Maps SDK does support custom colors and icons for markers, which may be sufficient for your purposes.

Edit: Stay tuned for iOS 11, which may offer this level of customization.

Marco
  • 6,692
  • 2
  • 27
  • 38
  • Thanks for the input. Do you know how the Google Maps SDK stand in comparison to native MapKit when it comes to performance? – Daniel Larsson Nov 28 '13 at 21:25
  • I'm no authority, but I cannot discern a major performance difference between the Google Maps SDK demo app and any app using MapKit. YMMV. – Marco Nov 28 '13 at 21:33
  • @Marco, performance boost favorable to MapKit or Google Maps SDK? – ScottyBlades Oct 05 '18 at 05:27
  • @ScottyBlades, my answer is from 2013 and possibly outdated at this point. I have no idea about a performance boost vis-a-vis MapKit or Google Maps SDK. – Marco Oct 30 '18 at 18:16
2

Another option is MBXMapKit, which is a MapBox library built atop Apple's MapKit, though it's geared for MapBox layers. It is separate from the MapBox iOS SDK, which is a ground-up rewrite meant to work like MapKit but not based on it.

incanus
  • 5,100
  • 1
  • 13
  • 20