Render MKMapView offscreen
Asked Answered
M

4

10

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?

Mcripley answered 6/4, 2017 at 3:48 Comment(0)
M
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.

Mcripley answered 18/4, 2017 at 5:22 Comment(0)
N
0

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

Niehaus answered 12/4, 2017 at 10:53 Comment(3)
I need to render the map into an image, but I don't need to show it in the UI.Mcripley
ok can you try one thing you can put the image in background instead of hiding it. i mean make a try in which you don't hide the image. Instead of that you can put a UIView obove the image and now see the break point does it come in this delegate or not? Coz what i want is to see if the image is behind the UIVIEW in that case its load request should be fulfilled and hence the delegate should be called but it won't be visible to the user.Niehaus
I have tried putting it in the background, as well as putting it offscreen. That doesn't work. But I was able to do what I want, I'll post an answer describing how.Mcripley
D
0

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.

Dichasium answered 17/4, 2017 at 11:33 Comment(1)
Thanks. This doesn't really work. Using another UIWindow is just like adding my map view to another, existing, view. The map is only rendered if it's visible on the screen. I was able to make it work though, I'm going to add my own answer.Mcripley
A
0

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).

Appulse answered 2/5, 2022 at 15:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.