首页 > 解决方案 > 使用 MKMapSnapshotter 创建可点击的地图预览

问题描述

我有一张地图,目前显示用户的当前位置,没有 pin 来显示他们的确切位置。我想让地图成为用户设定位置的照片。我只知道如何显示他们的设备在哪里,并且需要他们能够设置他们的操作基础。我不需要具体地址。我只需要他们居住的城市。

然后我需要能够点击图像。点击时,图像会使 MapKit 全屏并具有交互性。然后,他们可以放大地图并查看其他用户在哪里设置他们的运营基地。

我是编码新手,不知道如何让用户设置永久位置,即使他们在全国各地移动。我也不知道如何设置地图快照,并在点击以显示完整的地图视图时展开。

我目前只能询问是否可以激活位置服务,然后在加载时显示他们的地图视图。这是代码:

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

}

提前感谢您的帮助。我真的需要在这个应用程序上取得一些进展,我似乎找不到任何关于如何做到这一点的教程或网络结果。

编辑:

我尝试添加一个将我的 UIImage 转换为快照的函数。我可以返回一张图片,但它没有显示我的位置,而且它比我的 UIImage 小。我已经编辑了上面的代码以反映我所做的更改。我不知道我做错了什么。

标签: swiftgeolocationmapkituitapgesturerecognizermkmapsnapshotter

解决方案


在您的示例中,您正在创建一个CLLocationManager,但没有使用它。您正在使用CLLocation(). 这显然没有(有意义的)与之相关的坐标。确保提供有效的坐标。例如,有didUpdateLocations电话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)
    }
}

这产生:

在此处输入图像描述


不相关的观察:

  1. 您正在使用MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1). 我个人认为度数跨度并不是非常有用。我可能会建议使用仪表,例如

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

    或使用MKMapCamera

    options.camera = MKMapCamera(lookingAtCenter: location.coordinate, fromDistance: 1000, pitch: 0, heading: 0)
    
  2. showsBuildings如果您使用的地图类型为satellite. 文档说:

    mapType属性必须设置MKMapType.standard为 才能显示拉伸建筑物。

  3. 我不相信你必须设置scale. 文档说:

    此属性设置为对应于当前设备显示分辨率的默认值。

    此外,无论如何,此属性现在已被弃用。

  4. 不过,我建议设置size图像的。


推荐阅读