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)
orresult(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()
}
}