iOS14开发

iOS14开发-定位与地图

2021-07-11  本文已影响0人  YungFan

定位

CoreLocation 是 iOS 中用于设备定位的框架。通过这个框架可以实现定位进而获取位置信息如经度、纬度、海拔信息等。

模块与常见类

工作流程

  1. 创建CLLocationManager,设置代理并发起定位。
  2. 实现CLLocationManagerDelegate中定位成功和失败的代理方法。
  3. 在成功的代理方法中获取CLLocation对象并通过CLGeocoder进行反向地理编码获取对应的位置信息CLPlacemark
  4. 通过CLPlacemark获取具体的位置信息。

权限

授权对话框

前台定位

后台定位

精度控制

模拟器定位

由于定位需要 GPS,一般情况下需要真机进行测试。但对于模拟器,也可以进行虚拟定位,主要有 3 种方式。

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
    <!--安徽商贸职业技术学院 谷歌地球:31.2906511800,118.3623587000-->
    <wpt lat="31.2906511800" lon="118.3623587000">
        <name>安徽商贸职业技术学院</name>
        <cmt>中国安徽省芜湖市弋江区文昌西路24号 邮政编码: 241002</cmt>
        <desc>中国安徽省芜湖市弋江区文昌西路24号 邮政编码: 241002</desc>
    </wpt>
</gpx>

实现步骤

  1. 导入CoreLocation模块。
  2. 创建CLLcationManager对象,设置参数和代理,配置 Info.plist 并请求定位授权。
  3. 调用CLLcationManager对象的startUpdatingLocation()requestLocation()方法进行定位。
  4. 实现代理方法,在定位成功的方法中进行位置信息的处理。
import CoreLocation
import UIKit

class ViewController: UIViewController {
    // CLLocationManager
    lazy var locationManager = CLLocationManager()
    // CLGeocoder
    lazy var gecoder = CLGeocoder()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupManager()
    }

    func setupManager() {
        // 默认情况下每当位置改变时LocationManager就调用一次代理。通过设置distanceFilter可以实现当位置改变超出一定范围时LocationManager才调用相应的代理方法。这样可以达到省电的目的。
        locationManager.distanceFilter = 300
        // 精度 比如为10 就会尽量达到10米以内的精度
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        // 代理
        locationManager.delegate = self
        // 第一种:能后台定位但是会在顶部出现大蓝条(打开后台定位的开关)
        // 允许后台定位
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.requestWhenInUseAuthorization()
        // 第二种:能后台定位并且不会出现大蓝条
        // locationManager.requestAlwaysAuthorization()
    }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        // 以下2个方法都会调用代理方法
        // 1. 发起位置更新(定位)会一直轮询,耗电
        locationManager.startUpdatingLocation()
        // 2. 只请求一次用户的位置,省电
        // locationManager.requestLocation()
    }
}

extension ViewController: CLLocationManagerDelegate {
    // 定位成功
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        if let location = locations.last {
            // 反地理编码转换成具体的地址
            gecoder.reverseGeocodeLocation(location) { placeMarks, _ in
                // CLPlacemark -- 国家 城市 街道
                if let placeMark = placeMarks?.first {
                    print(placeMark)
                    // print("\(placeMark.country!) -- \(placeMark.name!) -- \(placeMark.locality!)")
                }
            }
        }
        // 停止位置更新
        locationManager.stopUpdatingLocation()
    }

    // 定位失败
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(error.localizedDescription)
    }
}

地图

准备工作

  1. 添加一个地图并设置相关属性。
  2. Info.plist 中配置定位权限。
  3. 创建 CLLocationManager 对象并请求定位权限。

基本使用

显示地图,同时显示用户所处的位置。点击用户的位置,显示一个气泡展示用户位置的具体信息。

import MapKit

class ViewController: UIViewController {
    @IBOutlet var mapView: MKMapView!
    lazy var locationManager: CLLocationManager = CLLocationManager()

    override func viewDidLoad() {
        super.viewDidLoad()

        setupMapView()
    }

    func setupManager() {
        locationManager.requestWhenInUseAuthorization()
        // 不需要发起定位
    }

    func setupMapView() {
        // 设置定位
        setupManager()
        // 地图类型
        mapView.mapType = .hybridFlyover
        // 显示兴趣点
        mapView.showsPointsOfInterest = true
        // 显示指南针
        mapView.showsCompass = true
        // 显示交通
        mapView.showsTraffic = true
        // 显示建筑
        mapView.showsBuildings = true
        // 显示级别
        mapView.showsScale = true
        // 用户跟踪模式
        mapView.userTrackingMode = .followWithHeading
    }
}

缩放级别

在之前功能的基础上实现地图的任意视角(“缩放级别”)。

// 设置“缩放级别”
func setRegion() {
    if let location = location {
        // 设置范围,显示地图的哪一部分以及显示的范围大小
        let region = MKCoordinateRegion(center: mapView.userLocation.coordinate, latitudinalMeters: 500, longitudinalMeters: 500)
        // 调整范围
        let adjustedRegion = mapView.regionThatFits(region)
        // 地图显示范围
        mapView.setRegion(adjustedRegion, animated: true)
    }
}

标注

在地图上可以添加标注来显示一个个关键的信息点,用于对用户的提示。

分类

创建模型

class MapFlag: NSObject, MKAnnotation {
    // 标题
    let title: String?
    // 副标题
    let subtitle: String?
    // 经纬度
    let coordinate: CLLocationCoordinate2D
    // 附加信息
    let urlString: String

    init(title: String?, subtitle: String?, coordinate: CLLocationCoordinate2D, urlString: String) {
        self.title = title
        self.subtitle = subtitle
        self.coordinate = coordinate
        self.urlString = urlString
    }
}

添加标注

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let flag = MapFlag(title: "标题", subtitle: "副标题", coordinate: CLLocationCoordinate2D(latitude: 31.2906511800, longitude: 118.3623587000), urlString: "https://www.baidu.com")
    mapView.addAnnotation(flag)
}
extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annotation = annotation as? MapFlag else {
            return nil
        }
        // 如果是用户的位置,使用默认样式
        if annotation == mapView.userLocation {
            return nil
        }
        // 标注的标识符
        let identifier = "marker"
        // 获取AnnotationView
        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKMarkerAnnotationView
        // 判空
        if annotationView == nil {
            annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            // 显示气泡
            annotationView?.canShowCallout = true
            // 左边显示的辅助视图
            annotationView?.leftCalloutAccessoryView = UIImageView(image: UIImage(systemName: "heart"))
            // 右边显示的辅助视图
            let button = UIButton(type: .detailDisclosure, primaryAction: UIAction(handler: { _ in
                print(annotation.urlString)
            }))
            annotationView?.rightCalloutAccessoryView = button
        }

        return annotationView
    }
}
extension ViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annotation = annotation as? MapFlag else {
            return nil
        }
        // 如果是用户的位置,使用默认样式
        if annotation == mapView.userLocation {
            return nil
        }
        // 标注的标识符
        let identifier = "custom"
        // 标注的自定义图片
        let annotationImage = ["pin.circle.fill", "car.circle.fill", "airplane.circle.fill", "cross.circle.fill"]
        // 获取AnnotationView
        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
        // 判空
        if annotationView == nil {
            annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
            // 图标,每次随机取一个
            annotationView?.image = UIImage(systemName: annotationImage.randomElement()!)
            // 显示气泡
            annotationView?.canShowCallout = true
            // 左边显示的辅助视图
            annotationView?.leftCalloutAccessoryView = UIImageView(image: UIImage(systemName: "heart"))
            // 右边显示的辅助视图
            let button = UIButton(type: .detailDisclosure, primaryAction: UIAction(handler: { _ in
                print(annotation.urlString)
            }))
            annotationView?.rightCalloutAccessoryView = button
            // 弹出的位置偏移
            annotationView?.calloutOffset = CGPoint(x: -5.0, y: 5.0)
        }

        return annotationView
    }
}

// 点击地图插入一个标注,标注的标题和副标题显示的是标注的具体位置
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    let touchPoint = touches.first?.location(in: mapView)
    // 将坐标转换成为经纬度,然后赋值给标注
    let coordinate = mapView.convert(touchPoint!, toCoordinateFrom: mapView)
    let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
    let gecoder = CLGeocoder()
    // 反地理编码转换成具体的地址
    gecoder.reverseGeocodeLocation(location) { placeMarks, _ in
        let placeMark = placeMarks?.first
        if let placeMark = placeMark {
            let flag = MapFlag(title: placeMark.locality, subtitle: placeMark.subLocality, coordinate: coordinate, urlString: "https://www.baidu.com")
            self.mapView.addAnnotation(flag)
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读