Home > Enterprise >  Overriding loadTiles in Mapkit Swift
Overriding loadTiles in Mapkit Swift

Time:10-12

I am trying to use custom tiles in MapKit with SwiftUI. I have found examples of how to do this if I have URLs for the tile, but I would like the tile to be dynamically and locally generated by the app. To do so, Apple's documentation seems to suggest I would need to override the loadTile function in MKTileOverlay. Per the documentation at https://developer.apple.com/documentation/mapkit/mktileoverlay/1452445-loadtile I have started defining my class as follows:

class TileOverlay: MKTileOverlay{
    override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void){
        //Now what?
    }
}

If I understand correctly this function would eventually return a tile, which is a 256x256 image. But how do I actually return this image? It appears that I have to return a closure that returns the image as a Data? object, but I was unsuccessful in finding how to convert a 256x256 image to such an object, nor was I able to determine how such a closure should work.

Any help is greatly appreciated!

CodePudding user response:

This is what you need to do:

  • Retrieve raw image data related to the path
  • Call at some point result(theRawImageData, nil) or result(nil, theErrorYouGot)

Then the caller of that method will get the closure parameters, and if there is no error, try do to: let image = UIImage(data: theRawImageData), and render it where it's needed.

If you already have an image in RAM, you can call jpegData(compressionQuality:), pngData() to get Data. But as you can see, you'll go from UIImage -> Data -> UIImage, there is a little extra work in the way. So try to keep Data instead of UIImage if you do some RAM caching (with NSCache for instance).

But, if you have a local image on disk (or on a distant URL), you can use Data(contentsOf:). Yet, it's not recommended to use that method, because it's synchrone (ie, blocks the current threads). Prefer using URLSession to do that.

override func loadTile(at path: MKTileOverlayPath, result: @escaping (Data?, Error?) -> Void){

    //If you use caching:
    if let imageDataInCache = ... {
        result(imageDataInCache, nil)
    } else {
        //Create an `URL` or a `URLRequest` from the `path`:
        let urlOrURLRequest = ...
        //Let's "fetch" the image:
        let task = URLSession.dataTask(with: urlOrURLRequest) { data, response, error in
            //Do additional work if needed, like checking `response`, etc.
            result(data, error)
        }
        task.resume()
    }
}
  • Related