zl程序教程

您现在的位置是:首页 >  其它

当前栏目

Swift:SwiftUI中MapKit的使用体验

体验 swift SwiftUI 使用
2023-09-27 14:25:57 时间
前言

ios中可以直接使用苹果官方提供的map——MapKit。在SwiftUI中如何使用MapKit网上有也有不少文章 但是大部分不详细 大部分只是简单的展示出地图。所以本文来详细的讲解一下如何使用MapKit的各项功能。

官方地址 developer.apple.com/documentati…


1、Map

在SwiftUI中可以直接使用Map组件 如下

import SwiftUI

import MapKit

struct ContentView: View {

 State var region MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 39.915, longitude: 116.397), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05))

 State var trackingMode MapUserTrackingMode.follow

 var body: some View {

 Map(coordinateRegion: $region, interactionModes: .all, showsUserLocation: true, userTrackingMode: $trackingMode)

struct ContentView_Previews: PreviewProvider {

 static var previews: some View {

 ContentView()

复制代码


这样可以直接显示地图 其中coordinateRegion就是当前的地图区域 中心点 经纬度的跨度等等 。

但是地图的各种功能怎么使用 答案是不能使用。通过官网的介绍


Map

A view that displays an embedded map interface.

...

Overview

A map view displays a region. Use this native SwiftUI view to optionally configure user-allowed interactions, display the user’s location, and track location.

Also create maps that display annotations at specific locations.

These annotated maps use one of the following types of annotation views:

MapPin

MapMarker

MapAnnotation

Maps only show annotation views of the same type, backed by a single collection.


可以看到这个view只是一个简单版本 可以展示一些预定义好的annotation 其他功能都无法使用。

所以一般情况下我们不使用这个 而是使用MKMapView。


2、MKMapView

MKMapView就无法直接像Map那样使用了 因为它并不继承View 而是继承UIView 所以需要用UIViewRepresentable来包装 代码如下


import SwiftUI

import MapKit

struct MapView: UIViewRepresentable {

 typealias UIViewType MKMapView

 func makeUIView(context: Context) - MKMapView {

 return MKMapView()

 func updateUIView(_ uiView: MKMapView, context: Context) {

 uiView.showsUserLocation true

 let loc CLLocationCoordinate2D(latitude: 39.915352, longitude: 116.397105)

 let region MKCoordinateRegion(center: loc, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))

 uiView.setRegion(uiView.regionThatFits(region), animated: true)

struct MapView_Previews: PreviewProvider {

 static var previews: some View {

 MapView()

复制代码


同样为MKMapView设置了初始的Region。然后我们就可以直接在SwiftUI中使用MapView了。


3、MKMapView的一些设置

下面是一些比较常用的设置

mapType 地图类型。包括标准、卫星等 具体看MKMapType这个枚举即可。isZoomEnabled 允许缩放isScrollEnabled 地图是否可以拖动isRotateEnabled 地图是否可以旋转isPitchEnabled 是否可以调整俯视视角showsScale 是否展示比例尺 只有当缩放地图的时候才显示 缩放完成后会自动隐藏 showsCompass 是否展示罗盘 当正上方是正北的时候罗盘自动隐藏 showsUserLocation 显示当前位置 需要有定位权限并且用户已经允许该权限 否则不显示 showsTraffic 显示交通信息showsBuildings 是否显示建筑 当camera视角不在正上方时 如果这个为true则会显示建筑物。视角在正上方则无差别 showsPointsOfInterest 是否显示POI 这个方法已经不推荐使用了

这里简单说一下ios simulator的使用 因为缩放、旋转、俯视需要双指操作 在simulator中需要按住option键可以进行双指相对操作 即两个点相对运动 比如缩放、旋转 同时按住option shift键才可以进行双指同向操作 比如俯视 或者比如在桌面上切屏。


4、处理Map上的操作

前面我们展示了地图 如果要在地图上的进行操作 比如点击 获取中心点等 这就需要使用UIViewRepresentable的Coordinator——协调器。

我们创建一个类 继承NSObject和MKMapViewDelegate 这里MKMapViewDelegate是一个protocol 定义了一些地图的操作有关的函数 比如


optional func mapViewDidChangeVisibleRegion(_ mapView: MKMapView)

复制代码


是地图可见范围改变时回调 比如拖动、缩放等行为。在这里我们作一些更新地图的操作 比如可以获取地图的中心点 代码如下


class MapCoordinator : NSObject, MKMapViewDelegate{

 func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {

 print(mapView.centerCoordinate)

复制代码


MKMapViewDelegate还有很多函数对应不同的回调 这里就不一一列举了。

然后我们需要重写UIViewRepresentable的makeCoordinator函数 新建一个MapCoordinator类的对象并返回 这样就可以通过context.coordinator来获取这个对象了。


然后将这个协调器绑定到map上 代码如下


import SwiftUI

import MapKit

struct MapView: UIViewRepresentable {

 func makeCoordinator() - MapCoordinator {

 MapCoordinator()

 typealias UIViewType MKMapView

 func makeUIView(context: Context) - MKMapView {

 let mapView MKMapView()

 mapView.delegate context.coordinator //绑定协调器到map

 return mapView

 func updateUIView(_ uiView: MKMapView, context: Context) {

 uiView.showsUserLocation true

 uiView.showsScale true

 uiView.showsBuildings false

 let loc CLLocationCoordinate2D(latitude: 39.915352, longitude: 116.397105)

 let region MKCoordinateRegion(center: loc, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))

 uiView.setRegion(uiView.regionThatFits(region), animated: true)

 class MapCoordinator : NSObject, MKMapViewDelegate{

 func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {

 print(mapView.centerCoordinate)

struct MapView_Previews: PreviewProvider {

 static var previews: some View {

 MapView()

复制代码


这样当拖动或缩放地图的时候 就会回调到mapViewDidChangeVisibleRegion 然后打印出当前中心点的经纬度。


点击操作

MKMapViewDelegate只能被动的接受地图的回调 如果我们主动操作怎么办 比如点击地图获取点击位置的经纬度 或者添加地图覆盖物 这时候需要为地图设置GestureRecognizer 并通过协调器进行处理。

首先创建一个UITapGestureRecognizer 并添加到map上 如下


let gRecognizer UITapGestureRecognizer(target: context.coordinator, action: #selector(MapCoordinator.touch(gestureReconizer:)))

mapView.addGestureRecognizer(gRecognizer)

复制代码


这里UITapGestureRecognizer的action执行的是MapCoordinator的touch函数 所以我们需要给MapCoordinator添加一个touch函数


class MapCoordinator : NSObject, MKMapViewDelegate{

 func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {

 print(mapView.centerCoordinate)

 objc func touch(gestureReconizer: UITapGestureRecognizer) {

 print( click )

复制代码


这个函数必须添加 objc标识 表示它可以供OC进行调用 因为最终实际上是OC调用的UITapGestureRecognizer的action。

然后我们点击地图 就可以看到打印出click了。


注意 只能在MKMapViewDelegate中使用 objc标识 如果我们直接给MapView添加一个touch函数 然后赋值给UITapGestureRecognizer 就会报错 因为UITapGestureRecognizer的action必须是一个被 objc标识的函数 而在MapView中不能给函数添加 objc。所以我们要通过MKMapViewDelegate来实现。

现在我们可以响应点击了 但是怎么获取点击位置的经纬度 touch函数只传入了一个UITapGestureRecognizer对象 通过它可以获取到点击的位置 如下


let point gestureReconizer.location(in: gestureReconizer.view)

复制代码


但是这个位置是屏幕位置 如果想换成经纬度还需要MKMapView才行 代码如下


let point gestureReconizer.location(in: gestureReconizer.view)

let loc mMapView?.convert(point, toCoordinateFrom: mMapView)

复制代码


但是在MapCoordinator中无法得到MKMapView对象 这里我添加了一个initMap(_ mapView : MKMapView)函数 在makeUIView阶段执行这个函数 将MKMapView对象传入 这样就可以获取经纬度了 最终整体代码如下


import SwiftUI

import MapKit

struct MapView: UIViewRepresentable {

 func makeCoordinator() - MapCoordinator {

 MapCoordinator()

 typealias UIViewType MKMapView

 func makeUIView(context: Context) - MKMapView {

 let mapView MKMapView()

 //添加GestureRecognizer

 let gRecognizer UITapGestureRecognizer(target: context.coordinator, action: #selector(MapCoordinator.touch(gestureReconizer:)))

 mapView.addGestureRecognizer(gRecognizer)

 //绑定协调器

 mapView.delegate context.coordinator

 //传入MKMapView对象

 context.coordinator.initMap(mapView)

 return mapView

 func updateUIView(_ uiView: MKMapView, context: Context) {

 uiView.showsUserLocation true

 uiView.showsScale true

 uiView.showsBuildings false

 let loc CLLocationCoordinate2D(latitude: 39.915352, longitude: 116.397105)

 let region MKCoordinateRegion(center: loc, span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))

 uiView.setRegion(uiView.regionThatFits(region), animated: true)

 class MapCoordinator : NSObject, MKMapViewDelegate{

 var mMapView : MKMapView?

 func initMap(_ mapView : MKMapView) {

 mMapView mapView

 func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {

 print(mapView.centerCoordinate)

 objc func touch(gestureReconizer: UITapGestureRecognizer) {

 let point gestureReconizer.location(in: gestureReconizer.view)

 let loc mMapView?.convert(point, toCoordinateFrom: mMapView)

 print(loc!)

struct MapView_Previews: PreviewProvider {

 static var previews: some View {

 MapView()

复制代码


5、总结

通过体验 感觉自带的MapKit使用起来并不是很便捷 而且我希望可以点击选中地图上的兴趣点 但是经过查找并没有发现任何可用的api。于是请教了ios的大佬 大佬说MapKit很少使用 因为MapKit功能不全 比如之前根本不支持路线 是近期才新增的 所以国内开发一般还是使用百度或高德。再联想这几天使用MapKit的各种问题 我果断放弃继续深入的想法。



字符和字符串 在swift中,String类型也是结构体,属于值类型,而不是引用类型。这一点,与OC是不一样的。 // main.
6.复合类型:元组(tuple) 注意:这里并没有像别的语言那样,把基本数据类型和用户自定义数据类型分类。