'Render MKMapView offscreen

I'm trying to render an MKMapView into an UIImage, without showing it on the screen. I init the map:

let mapView = MKMapView(frame: CGRect(x: 0, y: 0, width: 1000, height: 1000))
mapView.delegate = self

let region = MKCoordinateRegionMakeWithDistance(location.coordinate(), 1000, 1000)
mapView.setRegion(region, animated: false)

I also implemented the MKMapKitDelegate's method mapViewDidFinishLoadingMap(). This method never gets called, unless I add the map to the view hierarchy and make it visible. This means that setting its alpha to 0, or isHidden to true doesn't work (the map doesn't get loaded in this case). Help?



Solution 1:[1]

OK, here's my own solution.

  1. Implement the MKMapViewDelegate method mapViewDidFinishRenderingMap(_ mapView:, fullyRendered:). Be sure to use this method instead of mapViewDidFinishLoadingMap(_ mapView:)!

  2. Here's the wacky part. Add your map view to an existing and visible view (I used the view controller's own view), and position it offscreen, with only one pixel showing. This is important, since if the map has no visible pixels, it won't render. E.g.:

    let width = 1000
    let height = 1000
    
    let mapView = MKMapView(frame: CGRect(x: -width+1, y: -height+1, width: width, height: height))
    view.addSubview(mapView)
    
  3. There is no step 3. If you configure the map correctly, the delegate method mentioned in (1) will get called, and at that point you'll have your fully rendered map.

Solution 2:[2]

Can you tell what do you want to achieve by hiding? if your image is not visible the map won't load data and thats why its delegate is not called as also mentioned on the apple doc see the description below. it says it will be called when current request have been loaded in your case i think map is not visible so its request is not loaded and when you make it visible then the request is loaded and hence you can see that.

This method is called when the map tiles associated with the current request have been loaded. Map tiles are requested when a new visible area is scrolled into view and tiles are not already available. Map tiles may also be requested for portions of the map that are not currently visible. For example, the map view may load tiles immediately surrounding the currently visible area as needed to handle small pans by the user.

https://developer.apple.com/reference/mapkit/mkmapviewdelegate/1452291-mapviewdidfinishloadingmap

Solution 3:[3]

Apparently, MKMapView only loads the visible tiles of the map, so the mapView that you want to render offline must be „visible“.
As far as I know, a view is „visible“, if its window property is set, i.e. not nil (see progrmr's answer here).
Thus a possibility could be to setup an additional UIWindow that has your mapView as the root view. In this case, the window property of your mapView is set, and I believe the mapView would load the required tiles, so that it can be rendered, even if the additional UIWindow is not visible.
This is apparently similarly done by a pageViewController, see the comment of evanflash in the link above.

Solution 4:[4]

I had this issue last week, because I embedded the MapView in a Stackview, so it was automatically reduced when set to hidden. Maybe not your case but you should check the Size for you mapView.

Also, before I replaced the StackView with an other, setting alpha to something near zero -instead of using hidden-, did help (not checked with zero).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Community
Solution 3 Community
Solution 4 tontonCD