Create a clickable map preview using MKMapSnapshotter

二次信任 提交于 2021-01-29 04:52:02

问题


I have a map that currently shows the user's current location with no pin to show them exactly where they are. I want to make the map a photo of where the user's set location is. I only know how to show where their device is and need them to be able to set their base of operations. I don't need specific addresses. I just need the city where they reside.

I then need the image to be able to be tapped on. When tapped, the image makes the MapKit full screen and interactive. They can then zoom around the map and see where other users' set their base of operations.

I am new to coding and can't figure out how to allow the user to set a permanent location even if they move around the country. I also don't know how to set up the mapsnapshot and on top of that expand when tapped to show a fully working map view.

I am only currently able to ask if I can activate location services and then show their map view where they are when it is loaded. Here is the code:

import UIKit
import CoreLocation
import MapKit

class HomeTableViewController: UITableViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, CLLocationManagerDelegate, MKMapViewDelegate {

@IBOutlet weak var activityIndicator: UIActivityIndicatorView!

@IBOutlet weak var mapPreviewImageView: UIImageView!

@IBOutlet weak var mapView: MKMapView!

let manager = CLLocationManager()

override func viewDidLoad() {
    super.viewDidLoad()
    
    manager.desiredAccuracy = kCLLocationAccuracyBest // battery
    manager.delegate = self
    manager.requestWhenInUseAuthorization()
    manager.startUpdatingLocation()
    
    // Always adopt a light interface style.
    overrideUserInterfaceStyle = .light

    takeSnapShot()
    
}

override func numberOfSections(in tableView: UITableView) -> Int {
    // #warning Incomplete implementation, return the number of sections
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // #warning Incomplete implementation, return the number of rows
    return 7
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    if let location = locations.first {
        manager.stopUpdatingLocation()
        
        render(location)
    }
}

func render (_ location: CLLocation) {
    
    let coordinate = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
    
    let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    
    let region = MKCoordinateRegion(center: coordinate, span: span)
                                    
    mapView.setRegion(region, animated: true)
    
 }

 func takeSnapShot() {
    let location = CLLocation()
    
    let mapSnapshotOptions = MKMapSnapshotter.Options()

    let coordinate = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
    
    let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
    
    let region = MKCoordinateRegion(center: coordinate, span: span)
    mapSnapshotOptions.region = region

    // Set the scale of the image. We'll just use the scale of the current device, which is 2x scale on Retina screens.
    mapSnapshotOptions.scale = UIScreen.main.scale

    // Show buildings and Points of Interest on the snapshot
    mapSnapshotOptions.showsBuildings = true
    mapSnapshotOptions.mapType = .satellite

    let snapShotter = MKMapSnapshotter(options: mapSnapshotOptions)

    snapShotter.start() { snapshot, error in
        guard let snapshot = snapshot else {
            return
        }
        self.mapPreviewImageView.image = snapshot.image
    }
 }

}

Thanks for your help in advance. I really need to make some progress on this app and I can't seem to find any tutorials or web results on how to do this.

Edit:

I have tried adding a function that turns my UIImage into the snapshot. I am able to return an image but it doesn't show my location and it is smaller than my UIImage. I had edited the code above to reflect the changes I made. I don't know what I am doing wrong.


回答1:


In your example, you are creating a CLLocationManager, but not using it. You are using CLLocation(). That obviously has no (meaningful) coordinate associated with it. Make sure to supply a valid coordinate. For example, have didUpdateLocations call takeSnapshot:

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!

    private weak var snapshotter: MKMapSnapshotter?

    private lazy var manager: CLLocationManager = {
        let manager = CLLocationManager()
        manager.delegate = self
        manager.distanceFilter = 20
        return manager
    }()

    override func viewDidLoad() {
        super.viewDidLoad()

        if manager.authorizationStatus == .notDetermined {
            manager.requestWhenInUseAuthorization()
        }

        manager.startUpdatingLocation()
    }

    func takeSnapshot(of location: CLLocation) {
        snapshotter?.cancel()                         // cancel prior one, if any

        let options = MKMapSnapshotter.Options()

        options.camera = MKMapCamera(lookingAtCenter: location.coordinate, fromDistance: 1000, pitch: 0, heading: 0)
        options.mapType = .satellite
        options.size = imageView.bounds.size

        let snapshotter = MKMapSnapshotter(options: options)

        snapshotter.start() { snapshot, _ in
            self.imageView.image = snapshot?.image
        }

        self.snapshotter = snapshotter
    }
}

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last(where: { $0.horizontalAccuracy >= 0 } ) else { return }

        takeSnapshot(of: location)
    }
}

That yields:


Unrelated observations:

  1. You are using MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1). I personally do not find spans in degrees to be terribly useful. I might advise using meters, e.g.

    options.region = MKCoordinateRegion(center: location.coordinate, latitudinalMeters: 1000, longitudinalMeters: 1000)
    

    Or use a MKMapCamera:

    options.camera = MKMapCamera(lookingAtCenter: location.coordinate, fromDistance: 1000, pitch: 0, heading: 0)
    
  2. There is no point in using showsBuildings if you are using a map type of satellite. The docs say:

    The mapType property must be set to MKMapType.standard for extruded buildings to be displayed.

  3. I do not believe that you have to set the scale. The docs say:

    This property is set to a default value that corresponds to the resolution of the current device’s display.

    Besides, this property is now deprecated, anyway.

  4. I would suggest, though, to set the size of the image.



来源:https://stackoverflow.com/questions/64493563/create-a-clickable-map-preview-using-mkmapsnapshotter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!