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