CoreLocation 是 iOS 中用于设备定位的框架。经过这个框架能够实现定位进而获取位置信息如经度、纬度、海拔信息等。git
CoreLocation
模块中,使用时必须导入。CLLocationManager
:定位管理器,能够理解为定位不能本身工做,须要有个类对它进行全过程管理。CLLocationManagerDelegate
:定位管理代理,不论是定位成功与失败,都会有相应的代理方法进行回调。CLLocation
:表示某个位置的地理信息,包含经纬度、海拔等。CLPlacemark
:位置信息,包含的信息如国家、城市、街道等。CLGeocoder
:地理编码。CLLocationManager
,设置代理并发起定位。CLLocationManagerDelegate
中定位成功和失败的代理方法。CLLocation
对象并经过CLGeocoder
进行反向地理编码获取对应的位置信息CLPlacemark
。CLPlacemark
获取具体的位置信息。requestWhenInUseAuthorization
发起定位受权。requestAlwaysAuthorization
发起定位受权。Privacy - Location When In Use Usage Description
。requestWhenInUseAuthorization
发起定位受权。locationManager.allowsBackgroundLocationUpdates = true
。(1)Privacy - Location When In Use Usage Description
+ requestWhenInUseAuthorization
:能够后台定位,但会在设备顶部出现蓝条(刘海屏设备会出如今左边刘海)。 (2)Privacy - Location When In Use Usage Description
+ Privacy - Location Always and When In Use Usage Description
+ requestAlwaysAuthorization
:能够后台定位,不会出现蓝条。这种方式会出现 2 次受权对话框:第一次和前台定位同样,在赞成使用While Using App
模式后,继续使用定位才会弹出第二次,询问是否切换到Always
模式。swift
CLLocationManager
的accuracyAuthorization
属性获取当前的定位精度权限。CLLocationManager
的requestTemporaryFullAccuracyAuthorization(withPurposeKey purposeKey: String, completion: ((Error?) -> Void)? = nil)
方法申请一次临时精肯定位权限,其中purposeKey
为 Info.plist 中配置的Privacy - Location Temporary Usage Description Dictionary
字段下某个具体缘由的 key,能够设置多个 key 以应对不一样的定位使用场景。requestTemporaryFullAccuracyAuthorization
方法并不能用于申请定位权限,只能用于从模糊定位升级为精肯定位;若是没有得到定位权限,直接调用此 API 无效。Privacy - Location Default Accuracy Reduced
为YES
,此时申请定位权限的小地图中再也不有精度切换开关。须要注意 2 点:(1)若是发现该字段不是 Bool 型,须要以源码形式打开 Info.plist,而后手动修改<key>NSLocationDefaultAccuracyReduced</key>
为 Bool 型的值,不然没法生效。 (2)配置该字段后,若是 Info.plist 中还配置了Privacy - Location Temporary Usage Description Dictionary
,则仍能够经过requestTemporaryFullAccuracyAuthorization
申请临时的精肯定位权限,会再次弹出受权对话框进行确认。markdown
因为定位须要 GPS,通常状况下须要真机进行测试。但对于模拟器,也能够进行虚拟定位,主要有 3 种方式。并发
(1)新建一个gpx
文件,能够取名XXX.gpx
,而后将本身的定位信息填写进 xml 对应的位置。 (2)gpx
文件设置完成之后,首先须要运行一次 App,而后选择Edit Scheme
,在Options
中选择本身的gpx
文件,这样模拟器运行的时候就会读取该文件的位置信息。而后能够选择Debug
—>Simulate Location
或底部调试栏上的定位按钮进行gpx
文件或位置信息的切换。框架
<?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>
复制代码
CoreLocation
模块。CLLcationManager
对象,设置参数和代理,配置 Info.plist 并请求定位受权。CLLcationManager
对象的startUpdatingLocation()
或requestLocation()
方法进行定位。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)
}
}
复制代码
MapKit
模块中,使用时必须导入。显示地图,同时显示用户所处的位置。点击用户的位置,显示一个气泡展现用户位置的具体信息。dom
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
}
}
复制代码
在以前功能的基础上实现地图的任意视角(“缩放级别”)。ide
// 设置“缩放级别”
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)
}
}
}
复制代码