前言

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

官方地址

Map组件

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

import SwiftUI
import MapKitstruct 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.followvar 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。

MKMapView

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

import SwiftUI
import MapKitstruct MapView: UIViewRepresentable {typealias UIViewType = MKMapViewfunc makeUIView(context: Context) -> MKMapView {return MKMapView()}func updateUIView(_ uiView: MKMapView, context: Context) {uiView.showsUserLocation = truelet 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了。

地图设置

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

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

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

事件处理

前面我们展示了地图,如果要在地图上的进行操作,比如点击,获取中心点等,这就需要使用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 MapKitstruct MapView: UIViewRepresentable {func makeCoordinator() -> MapCoordinator {MapCoordinator()}typealias UIViewType = MKMapViewfunc makeUIView(context: Context) -> MKMapView {let mapView = MKMapView()mapView.delegate = context.coordinator  //绑定协调器到mapreturn mapView}func updateUIView(_ uiView: MKMapView, context: Context) {uiView.showsUserLocation = trueuiView.showsScale = trueuiView.showsBuildings = falselet 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 MapKitstruct MapView: UIViewRepresentable {func makeCoordinator() -> MapCoordinator {MapCoordinator()}typealias UIViewType = MKMapViewfunc makeUIView(context: Context) -> MKMapView {let mapView = MKMapView()//添加GestureRecognizerlet 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 = trueuiView.showsScale = trueuiView.showsBuildings = falselet 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开发】SwiftUI中使用MapKit实现地图功能相关推荐

  1. Objective-C依然占C位,Swift和SwiftUI在iOS 15中的使用情况

    作者 | Alexandre Colucci 译者 | 弯月 出品 | CSDN(ID:CSDNnews) iOS 15 已经发布几个月了,在本文中,我们来分析一下 iOS 15 的内置应用,看看 i ...

  2. swift和swiftui_在swiftui中查看布局和演示

    swift和swiftui 您一直在等待的完整SwiftUI 2文档 (The Complete SwiftUI 2 Documentation You've Been Waiting For) At ...

  3. 【IOS开发】SwiftUI中的反斜杠\含义以及用法

    1.@Environment(\.colorScheme)中的\是什么意思? 在 SwiftUI 中,使用 \(反斜杠)可以访问环境中的键路径(key path),这样你就可以获取环境变量的值.键路径 ...

  4. 使用自定义字体升级您的 SwiftUI 应用程序教程,如何在 SwiftUI 中添加自定义字体

    大家好,我叫 Izzy,在这篇短文中,我想向您展示如何将新字体添加到您的 SwiftUI 项目中.XCode 项目的默认字体易于阅读,通常适用于很多用例,但有时添加另一种字体以获得更多细节会很好. 先 ...

  5. 使用Xcode13进行Swift开发

    使用Xcode13进行Swift开发 1.软件 Xcode 2.快捷键 3.应用功能 3.1 VStack,HStack,ZStack 3.2 frame,foregroundColor,corner ...

  6. 《Swift开发实战》——第2章,第2.4节函数和闭包

    本节书摘来自异步社区<Swift开发实战>一书中的第2章,第2.4节函数和闭包,作者 李宁,更多章节内容可以访问云栖社区"异步社区"公众号查看 2.4 函数和闭包 在本 ...

  7. 《Swift开发实战》——第16章,第16.2节下标脚本用法

    本节书摘来自异步社区<Swift开发实战>一书中的第16章,第16.2节下标脚本语法,作者 李宁,更多章节内容可以访问云栖社区"异步社区"公众号查看 16.2 下标脚本 ...

  8. iOS开发 Xcode8中遇到的问题及改动

    2019独角兽企业重金招聘Python工程师标准>>> iOS开发 Xcode8中遇到的问题及改动 新版本发布总会有很多坑,也会有很多改动. 一个一个填吧... 一.遇到的问题 1. ...

  9. 《Swift开发实战》——导读

    本节书摘来自异步社区<Swift开发实战>一书中的目录,作者 李宁,更多章节内容可以访问云栖社区"异步社区"公众号查看 目 录 内容简介 前 言 第1章 工欲善其事,必 ...

最新文章

  1. 详细谈电脑ip、域名、内网、外网、localhost、127.0.0.1、网关等通讯基础知识(易懂)
  2. Go 支持Protocol Buffers的配置
  3. DNS智能解析的另类使用 让搜索引擎更快更好的收录您的网站
  4. android json 解析,android json解析 泛型
  5. 『数据库』数据库的查询可不是只知道Select就可以的--关系数据库系统的查询处理
  6. OLAP引擎:基于Presto组件进行跨数据源分析
  7. Magento教程 17:Magento功能导览(1) 会员功能
  8. 原linux的字符文件作用,linux特殊字符及其作用大全
  9. Sublime2 配置python 和 c++
  10. testlink php nginx,linux环境部署testlink步骤说明
  11. matlab头模型图像,用Matlab解《2013年数据建模比赛》图像碎片拼接题
  12. M8的USB工作模式切换工具
  13. 计算机学院方阵入场词,学校运动会方阵入场解说词
  14. C语言的源代码文件、目标文件和可执行文件
  15. Hamcrest 精萃
  16. 什么是web services?它有什么作用,以及它如何实现?
  17. HTN规划 jshop2
  18. 第三章 微分中值定理与导数的应用
  19. sau交流学习社区第三方登陆github--oauth来实现用户登录
  20. Matlab——行星轨道问题

热门文章

  1. 【1万字搞懂】到底什么是CRM系统以及怎么选
  2. Successful Lisp - Cover
  3. Revit二次开发-修改标注线尺寸界线的方向
  4. python图片加水印_怎么在照片上加水印-用Python实现给图片增加水印
  5. Linux集群间免密登录
  6. 玩日志的你不了解 Filebeat ,就像搞结拜不认识关二爷!深度解析 Filebeat 工作原理,轻松玩转大数据!
  7. 计算机设计大赛科学,2018年中国大学生计算机设计大赛举行
  8. 创维代工M302A(2+8)、E900V22E(2+8)、E900V21E、E900V21C晶晨S905L3-B通刷卡刷固件及教程分享
  9. 2023复试题预测:送你一堆冰墩墩,拿走不谢~文都管联院
  10. 贵阳重庆两日往返游计划书