美文网首页MKMapKit
MapKit框架详细解析(九) —— 地图特定区域放大和创建自定

MapKit框架详细解析(九) —— 地图特定区域放大和创建自定

作者: 刀客传奇 | 来源:发表于2020-04-26 00:10 被阅读0次

    版本记录

    版本号 时间
    V1.0 2020.04.26 星期日

    前言

    MapKit框架直接从您的应用界面显示地图或卫星图像,调出兴趣点,并确定地图坐标的地标信息。接下来几篇我们就一起看一下这个框架。感兴趣的看下面几篇文章。
    1. MapKit框架详细解析(一) —— 基本概览(一)
    2. MapKit框架详细解析(二) —— 基本使用简单示例(一)
    3. MapKit框架详细解析(三) —— 基本使用简单示例(二)
    4. MapKit框架详细解析(四) —— 一个叠加视图相关的简单示例(一)
    5. MapKit框架详细解析(五) —— 一个叠加视图相关的简单示例(二)
    6. MapKit框架详细解析(六) —— 添加自定义图块(一)
    7. MapKit框架详细解析(七) —— 添加自定义图块(二)
    8. MapKit框架详细解析(八) —— 添加自定义图块(三)

    开始

    首先看下主要内容:

    了解如何使用功能强大的MapKit框架来构建交互式地图,显示位置详细信息并启动用于行车路线的地图。文章来自翻译

    下面看下写作环境

    Swift 5, iOS 13, Xcode 11

    MapKitiOS设备上可用的功能强大的API,可轻松显示地图,标记位置,增强自定义数据甚至在顶部绘制路线或其他形状。

    在此MapKit教程中,您将制作HonoluluArt,此应用程序可缩放到Honolulu中的位置并在地图上标记公共艺术品。 您将实现标记的标注详细信息按钮,以启动Maps应用并打开艺术品的行车路线。 然后,您将从Honolulu city数据门户网站解析GeoJSON文件,以提取公共艺术品特征并将其标记在地图上。

    在此过程中,您将学习如何:

    • MapKit地图添加到您的应用。
    • 缩放到特定位置。
    • 解析GeoJSON数据以创建自定义地图注释。

    打开开始项目中的Main.storyboard,然后从Object library中将Map Kit View拖到场景的中间。 将Map View限制为父视图(而不是“安全区域”),并在所有边缘上将其设置为0,以便将其扩展到带缺口设备的“安全区域”中。

    构建并运行。 现在,您可以使用Apple Maps完全缩放和平移的地图,显示您当前位置的大陆!

    到目前为止,很好,是吗?

    您想放大特定的区域。 为此,您需要获取编码!

    打开ViewController.swift并在import UIKit语句下面添加以下内容:

    import MapKit
    

    接下来,您需要在ViewController.swift中的MKMapView设置outlet

    viewDidLoad()之前添加以下outlet代码:

    @IBOutlet private var mapView: MKMapView!
    

    然后,转到Main.storyboard并将Map View链接到新的outlet


    Setting the Visible Area

    回到ViewController。找到viewDidLoad(),并在方法末尾添加以下内容:

    // Set initial location in Honolulu
    let initialLocation = CLLocation(latitude: 21.282778, longitude: -157.829444)
    

    您将使用此设置将地图视图的起始坐标设置为Honolulu的一个点。

    在告诉地图要显示什么内容时,提供纬度和经度就足以使地图居中。但是,您还必须指定要显示的矩形区域,以获得正确的缩放级别。

    ViewController.swift的末尾添加以下私有扩展:

    private extension MKMapView {
      func centerToLocation(
        _ location: CLLocation, 
        regionRadius: CLLocationDistance = 1000
      ) {
        let coordinateRegion = MKCoordinateRegion(
          center: location.coordinate,
          latitudinalMeters: regionRadius,
          longitudinalMeters: regionRadius)
        setRegion(coordinateRegion, animated: true)
      }
    }
    

    location参数是中心点。根据regionRadius的距离,该区域将具有南北和东西跨度,默认为1000米,略多于半英里,这对于在GeoJSON文件中绘制公共艺术品数据非常有用。

    setRegion(_:animated:)告诉MKMapView显示由MKCoordinateRegion表示的区域。地图视图会自动将当前视图转换为所需的区域,带有一个整洁的缩放动画,不需要额外的代码!

    viewDidLoad()中,在方法的末尾添加以下行:

    mapView.centerToLocation(initialLocation)
    

    这将调用helper方法以在启动时放大到initialLocation

    构建和运行。你会发现自己在Waikiki. Aloha的中心。


    Constraining the Camera

    到目前为止,你可以去地图上任何你想去的地方,但我们只对Oahu兴趣。你还可以在缩放地图到目前为止,岛屿只有几个像素!MapKit能够约束用户在指定区域内平移和缩放地图。

    注意:要放大模拟器,按住Option并拖动地图视图。
    同样在viewDidLoad()中,在方法的末尾添加以下行:

        let oahuCenter = CLLocation(latitude: 21.4765, longitude: -157.9647)
        let region = MKCoordinateRegion(
          center: oahuCenter.coordinate,
          latitudinalMeters: 50000,
          longitudinalMeters: 60000)
        mapView.setCameraBoundary(
          MKMapView.CameraBoundary(coordinateRegion: region),
          animated: true)
        
        let zoomRange = MKMapView.CameraZoomRange(maxCenterCoordinateDistance: 200000)
        mapView.setCameraZoomRange(zoomRange, animated: true)
    

    构建和运行。现在当你缩小的时候,你不能让Oahu像以前一样小。你只能平移地图视图来查看岛的北部、南部、东部和西部的边缘。

    MapKit使用一个内部摄像机来确定地图的视点在哪里,视场的范围有多大,并使视点移动。当您指定一个具有中心和区域矩形的摄像机边界时,摄像机中心始终位于此区域内。

    如果用户试图移出该区域,则视图不会进一步滚动。让他们专注于你想让他们看到的地方。6万米宽、5万米高的区域大小很好地限制了Oahu的地图视野。

    MapKit视图摄像机在缩放时跟踪它与视图中心的距离。设置相机的缩放范围最大中心距离约束限制了视野缩小的距离和Oahu岛变得多么小。这在向地图添加兴趣点(points of interest)时更有帮助,这是下一步!


    Obtaining Public Art Data

    下一步是围绕当前位置绘制有趣的数据。

    嗯,这取决于你现在的位置。和许多城市一样,Honolulu也有一个开放的数据门户 Open Data Portal,以改善公众对政府数据的访问。

    Honolulu数据门户的一个方便特性是能够以GeoJSON格式导出数据集——一种JSON格式,用于表示地理空间特性及其元数据。MapKit最近的一个特性使得在您的应用程序中使用GeoJSON数据变得超级容易,您很快就会看到这一点。

    注意:在您完成本教程之后,查看附近的城市是否有另一个您可以使用的GeoJSON格式的数据集。一旦你看到它是多么容易,你可能会发现它有趣的绘图在同一地图上的不同数据集。

    在本教程中,您将使用檀香山公共艺术Honolulu Public Art数据集。为了简单起见,我已经从门户导出了这些数据,并将其包含在starter项目中,当然,为了便于阅读,这些数据经过了良好的格式化。这是我最起码能做的。

    要了解此数据集中的项,请打开Xcode编辑器中的PublicArt.geojson。在顶层你会看到:

    {
      "type": "FeatureCollection",
      "features": [ ... ]
    }
    

    features的值是类型为FeatureGeoJSON对象数组。每个对象都包含一些标准键,以及一个properties字典,其中包含由数据门户创建的一些自定义字段。geometry键是你找到特征的坐标的地方。

    看看第一个:

    {
      "type":"Feature",
      "properties":{
        "location":"Lester McCoy Pavilion",
        "latitude":"21.290824",
        "description":"...",
        "thumb":null,
        "credit":"Funded by the Works Progress Administration",
        "objectid":"1930.01.01",
        "creator":"Robert Lee Eskridge",
        "longitude":"-157.85131",
        "imagefile":"http://....JPG",
        "date":"1935",
        "discipline":"Mural",
        "title":"The Makahiki Festival - The Makai Mural",
        "access":"Limited"
      },
      "geometry":{
        "type":"Point",
        "coordinates":[-157.85131,21.290824]
      }
    }
    

    1. GeoJSON Properties

    不要太担心GeoJSON数据格式。您很快就会看到,导入它基本上是自动的!基本部分是属性字典和坐标。

    对于本教程,您将只使用几个属性:艺术品的位置名称、规程、标题、纬度和经度:

    • Location name: Lester McCoy Pavilion.
    • Discipline: Mural.
    • Title: The Makahiki Festival – The Makai Mural.
    • Latitude: 21.290824.
    • Longitude: -157.85131.

    注意:如果你想了解更多关于格式的信息,你可以探索整个世界。官方网站official website是有点稀疏,RFC是高度技术性的,但维基百科Wikipedia article的文章是可读的。

    它是一种广泛支持的格式,适用于所有地理空间方面的内容。一些有用的工具包括geojson.io是一个测试和编辑GeoJSON数据的有用站点,GitHub将自动在地图上的存储库中呈现任何.geojson文件。

    在本教程的后面,您将使用整个数据集。但首先,为了直接进入MapKit,您将在地图上绘制一个艺术品。


    Showing Artwork on the Map

    PublicArt.geojson,按下Command-L,跳到第1354行。这是威基基公园(Waikiki Gateway Park)里的大卫王卡拉卡瓦King David Kalakaua的青铜雕像。

    Photo of King David Kalakaua statue, by Wally Gobetz

    这个项目的属性是:

    • Location name: Waikiki Gateway Park
    • Discipline: Sculpture
    • Title: King David Kalakaua
    • Latitude: 21.283921
    • Longitude: -157.831661

    要在地图视图中显示这一点,您必须创建一个map annotationmap annotation是绑定到特定位置的小块信息。苹果的Maps应用程序通常将它们表示为小图钉。

    要创建注释annotations,您需要创建一个符合MKAnnotation的类,将该注释添加到地图中,并告诉地图该如何显示该注释。

    1. The Artwork Class

    首先,在“项目导航器”中右键单击HonoluluArt文件夹,选择New File…,选择Swift File,并将新文件命名为Artwork.swift

    在编辑器中打开Artwork.swift,并添加以下import Foundation

    import MapKit
    
    class Artwork: NSObject, MKAnnotation {
      let title: String?
      let locationName: String?
      let discipline: String?
      let coordinate: CLLocationCoordinate2D
    
      init(
        title: String?,
        locationName: String?,
        discipline: String?,
        coordinate: CLLocationCoordinate2D
      ) {
        self.title = title
        self.locationName = locationName
        self.discipline = discipline
        self.coordinate = coordinate
    
        super.init()
      }
    
      var subtitle: String? {
        return locationName
      }
    }
    

    为了符合MKAnnotation, Artwork必须子类化NSObject,因为MKAnnotation是一个NSObjectProtocol

    MKAnnotation需要coordinate属性。如果您希望注释视图在用户点击标记时显示标题和副标题,那么您的类还需要名为titlesubtitle的属性。

    Artwork类完全可以存储名为titlecoordinate的属性,而不存储PublicArt.geojson属性自然地映射到subtitle。要符合MKAnnotation,可以将subtitle作为返回locationName的计算属性。

    MKAnnotation协议属性titlesubtitle被定义为可选的字符串,但是您可能想知道为什么要使用String?作为locationNamediscipline属性的类型。由于这些属性是从外部数据源PublicArt.geojson设置文件,你不能保证它们总是存在。最好是安全一点。

    你会使用MKAnnotation对象的title,locationNamecoordinate属性,但它的discipline属性是什么呢?您将在本教程的后面找到答案!

    2. Adding an Annotation

    接下来,您将为您想要绘制的每个艺术品向地图视图添加一个Artwork对象。现在,您只添加了一个artwork,因此切换到ViewController.swift并在viewDidLoad()的末尾添加以下行:

    // Show artwork on map
    let artwork = Artwork(
      title: "King David Kalakaua",
      locationName: "Waikiki Gateway Park",
      discipline: "Sculpture",
      coordinate: CLLocationCoordinate2D(latitude: 21.283921, longitude: -157.831661))
    mapView.addAnnotation(artwork)
    

    在这里,您创建一个新的Artwork对象,并将其作为注释添加到地图视图中。MKMapView还提供了addAnnotations(_:),在本教程后面的内容中,当您有一个要添加到map视图中的annotation数组时,您将会用到它。

    构建和运行。现在你可以看到大卫王卡拉卡瓦(King David Kalakaua)的雕像就在Waikiki的门口!

    默认的annotation标记视图显示标题位于标记下方的位置。选择标记。它变大,现在也显示子标题:

    好吧,这没什么,但你已经看到,在其他应用程序中点击一个标记会显示一个标注(callout):一个小小的方形语音气泡。为此,您必须配置annotation视图。

    3. Configuring the Annotation View

    配置annotation视图的一种方法是实现map视图的mapView(_:viewFor:)代理方法。在这个代理方法中,您的工作是返回MKAnnotationView的一个实例,作为annotation的一个可视指示器。

    在本例中,ViewController是地图视图的代理。为了避免混乱和提高可读性,您将创建一个ViewController扩展。

    ViewController.swift的底部添加如下代码:

    extension ViewController: MKMapViewDelegate {
      // 1
      func mapView(
        _ mapView: MKMapView, 
        viewFor annotation: MKAnnotation
      ) -> MKAnnotationView? {
        // 2
        guard let annotation = annotation as? Artwork else {
          return nil
        }
        // 3
        let identifier = "artwork"
        var view: MKMarkerAnnotationView
        // 4
        if let dequeuedView = mapView.dequeueReusableAnnotationView(
          withIdentifier: identifier) as? MKMarkerAnnotationView {
          dequeuedView.annotation = annotation
          view = dequeuedView
        } else {
          // 5
          view = MKMarkerAnnotationView(
            annotation: annotation,
            reuseIdentifier: identifier)
          view.canShowCallout = true
          view.calloutOffset = CGPoint(x: -5, y: 5)
          view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
        }
        return view
      }
    }
    

    你要做的是:

    • 1) mapView(_:viewFor:)在处理table view时,对于添加到地图中的每一个annotation都会被调用,比如tableView(_:cellForRowAt:),以返回每个注释的视图。
    • 2) 你的应用可能会使用其他的注解,比如用户位置,所以检查这个注解是否是一个Artwork对象。如果不是,则返回nil以允许地图视图使用其默认的注释视图。
    • 3) 将每个视图创建为MKMarkerAnnotationView。在本教程的后面,您将创建MKAnnotationView对象来显示图像,而不是标记。
    • 4) 与tableView(_:cellForRowAt:)类似,地图视图重用了不再可见的注释视图。因此,在创建新注释视图之前,要检查是否有可重用的注释视图可用。当您将一个可重用的注释从队列中取出时,您给它一个标识符。如果您有多种类型的注释,请确保每种注释都有唯一的标识符。这和tableView(_:cellForRowAt:)中的cell标识符是一样的。
    • 5) 在这里,如果无法将注释视图从队列中dequeue,则创建一个新的MKMarkerAnnotationView对象。它使用Artwork类的标题和副标题(title and subtitle)属性来确定在callout中显示什么。

    4. The Map View Delegate

    剩下的就是将ViewController设置为map视图的代理。你可以在Main.storyboard中这样做。但如果你在代码中做,它更明显。在ViewController.swift中,添加这一行到viewDidLoad(),之前的声明,创建artwork

    mapView.delegate = self
    

    这是它!构建和运行。点击标记,弹出callout气泡:

    mapView(_:viewFor:)callout配置为在右侧包含一个detail disclosure info按钮,但是点击该按钮还没有完成任何操作。你可以实现它来显示一个包含更多信息的alert或者打开一个细节视图控制器。

    这是第三种选择。当用户点击信息按钮时,你的应用程序就会启动Maps应用程序,完成从模拟用户位置到艺术品的驾驶、步行和交通指示!


    Launching the Maps App

    为了提供这一伟大的用户体验,打开Artwork.swift并添加这一import声明:

    import Contacts
    

    这将添加Contacts框架,该框架包含字典键常量,如CNPostalAddressStreetKey,用于在需要设置地址、城市或州字段时使用。

    要告诉Maps应用程序要去哪里,您必须传递一个MKMapItem给它。这个类描述地图上的一个兴趣点。要创建一个,您必须首先创建一个MKPlacemark来描述这个点。

    接下来,将以下属性添加到类中:

    var mapItem: MKMapItem? {
      guard let location = locationName else {
        return nil
      }
    
      let addressDict = [CNPostalAddressStreetKey: location]
      let placemark = MKPlacemark(
        coordinate: coordinate,
        addressDictionary: addressDict)
      let mapItem = MKMapItem(placemark: placemark)
      mapItem.name = title
      return mapItem
    }
    

    您可以使用现有的位置信息作为地址来创建MKPlacemark。然后创建并配置与Maps通信所需的MKMapItem

    1. Handling the Callout

    接下来,您必须告诉MapKit当用户点击callout按钮时应该做什么。打开ViewController.swift。并将此方法添加到MKMapViewDelegate扩展:

    func mapView(
      _ mapView: MKMapView,
      annotationView view: MKAnnotationView,
      calloutAccessoryControlTapped control: UIControl
    ) {
      guard let artwork = view.annotation as? Artwork else {
        return
      }
    
      let launchOptions = [
        MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
      ]
      artwork.mapItem?.openInMaps(launchOptions: launchOptions)
    }
    

    当用户点击地图注释标记时,callout显示一个info按钮。如果用户点击这个信息按钮,iOS会调用mapView(_:annotationView: calloutAccessoryControlTap:)

    在此方法中,获取此tap引用的Artwork对象,然后通过创建关联的MKMapItem并在map item上调用openInMaps(launchOptions:)来启动Maps

    注意,您正在向这个方法传递一个字典。这允许您指定几个不同的选项。这里,DirectionsModeKey设置为Driving

    这将导致Maps显示从用户当前位置到此位置的驾驶方向。

    注意:查看MKMapItem文档documentation以查看其他启动选项字典键和openMaps(with:launchOptions:)方法,该方法允许您传递一个MKMapItem对象数组。

    2. Setting Your Simulated Location

    在你构建和运行之前,你应该移动到Honolulu。那么,只需将您的模拟位置设置为Honolulu

    Xcode中,点击“Product►Scheme►Edit Scheme…”,从左侧菜单中选择“Run”,然后选择“Options”选项卡。选中Allow Location Simulation,并选择Honolulu, HI,美国作为默认位置。然后点击Close按钮:

    构建和运行。你会看到Waikiki的地图像以前一样放大了。点击标记,然后点击callout中的info按钮,就可以看到它启动Maps,显示雕像的位置和前往雕像的驾驶方向:

    这需要用你最喜欢的热带饮料来庆祝一下!

    注意:当你第一次打开Maps时,它会提示你允许Maps访问你的位置 —— 点击Allow While Using App —— 并显示一个安全警告。


    Decoding GeoJSON with MKGeoJSONDecoder

    现在您已经知道了如何在地图上显示一个artwork,以及如何从标记的callout信息按钮启动Maps,现在是时候将数据集解析为Artwork对象数组了。然后将它们作为注释添加到地图视图中,以显示位于当前地图区域的所有艺术品。

    MapKitMKGeoJSONDecoder,这是一个非常有用的功能。它可以解码GeoJSON数据并返回一个实现MKGeoJSONObject协议的对象数组。MapKit还提供了一个实现此协议的具体类:MKGeoJSONFeature,这是本教程所需的全部内容。

    将这个可失败的初始化程序添加到Artwork.swift中,初始化器下方:

    init?(feature: MKGeoJSONFeature) {
      // 1
      guard 
        let point = feature.geometry.first as? MKPointAnnotation,
        let propertiesData = feature.properties,
        let json = try? JSONSerialization.jsonObject(with: propertiesData),
        let properties = json as? [String: Any] 
        else {
          return nil
      }
    
      // 3
      title = properties["title"] as? String
      locationName = properties["location"] as? String
      discipline = properties["discipline"] as? String
      coordinate = point.coordinate
      super.init()
    }
    

    你要做的是:

    • 1) MKGeoJSONFeature具有表示与该特性关联的一个或多个形状的geometry属性。PublicArt.geojson的所有特点是点位置,MapKit为您创建了一个MKPointAnnotation。在这里,您可以找到作为CLLocationCoordinate2D的坐标。
    • 2) 接下来,读取特性的properties,哪些是Data?类型并包含一个序列化的JSON字典。使用JSONSerialization将数据解码到一个Swift字典中。
    • 3) 现在已经对属性进行了解码,您可以从字典值中设置适当的Artwork属性。

    1. Making Annotations

    要使用这个初始化器,打开ViewController.swift并添加以下属性,一个数组来保存来自GeoJSON文件的Artwork对象到类中:

    private var artworks: [Artwork] = []
    

    接下来,将以下helper方法添加到类中:

    private func loadInitialData() {
      // 1
      guard 
        let fileName = Bundle.main.url(forResource: "PublicArt", withExtension: "geojson"),
        let artworkData = try? Data(contentsOf: fileName) 
        else {
          return
      }
    
      do {
        // 2
        let features = try MKGeoJSONDecoder()
          .decode(artworkData)
          .compactMap { $0 as? MKGeoJSONFeature }
        // 3
        let validWorks = features.compactMap(Artwork.init)
        // 4
        artworks.append(contentsOf: validWorks)
      } catch {
        // 5
        print("Unexpected error: \(error).")
      }
    }
    

    以下是你在这段代码中要做的事情:

    • 1) 首先,你要读取PublicArt.geojson转换为Data对象。
    • 2) 使用MKGeoJSONDecoder获取一个GeoJSON对象数组,但仅使用compactMap保存MKGeoJSONFeature的实例。
    • 3) 使用添加的可失败初始化器将MKGeoJSONFeature对象转换为Artwork对象,并再次使用compactMap
    • 4) 将产生的validWorks添加到artworks数组中。
    • 5) 因为MKGeoJSONDecoderdecode(_:)方法会抛出一个错误,所以您可以捕获它并将错误打印到Xcode控制台。

    Plotting the Artwork

    您现在在数据集中有了一个所有公共artwork的数组,您将把它添加到地图中。

    还在ViewController.swift中,在viewDidLoad()结尾添加以下代码:

    loadInitialData()
    mapView.addAnnotations(artworks)
    

    注意:一定要使用复数形式的addAnnotations,而不是单数形式的addAnnotation!

    删除创建单个King David Kalakaua地图注释的行。loadInitialData()创建了artworks数组,现在不需要它们了。

    构建和运行。检查所有的标记!

    移动地图,可以看到其他标记出现。点击一个标记来打开它的callout气泡,然后点击它的信息按钮来启动Maps。是的,你对卡拉卡瓦国王雕像所做的一切都与所有新的artwork有关!

    如果您担心在不可见的情况下向地图添加注释,请不要这样做!苹果建议立即添加所有注释Apple recommends adding all the annotations right away,无论它们在地图区域是否可见。当您移动地图时,它会自动显示可见的注释。

    现在,您已经构建了一个应用程序,该应用程序将一个GeoJSON文件解析为一个artworks数组,然后将其作为注释标记显示,并带有一个callout信息按钮来启动Maps

    但等一下,还有一些亮点要添加。


    Customizing Annotations

    还记得Artwork类中的discipline属性吗?它的值比如SculptureMural。事实上,最多的disciplineSculpture, Plaque, Mural and Monument

    标记的颜色编码很容易,所以大多数学科都有自己的彩色标记,而所有其他的disciples都有绿色标记。

    1. Markers with Color-Coding and Text

    Artwork.swift中,添加此属性:

    var markerTintColor: UIColor  {
      switch discipline {
      case "Monument":
        return .red
      case "Mural":
        return .cyan
      case "Plaque":
        return .blue
      case "Sculpture":
        return .purple
      default:
        return .green
      }
    }
    

    现在,您可以继续向mapView(_:viewFor:)添加代码,但这会使视图控制器变得混乱。还有一种更优雅的方法,类似于您可以为table view单元格所做的事情。创建一个名为ArtworkViews.swift的新Swift文件,并在import语句下面添加以下代码:

    import MapKit
    
    class ArtworkMarkerView: MKMarkerAnnotationView {
      override var annotation: MKAnnotation? {
        willSet {
          // 1
          guard let artwork = newValue as? Artwork else {
            return
          }
          canShowCallout = true
          calloutOffset = CGPoint(x: -5, y: 5)
          rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
    
          // 2
          markerTintColor = artwork.markerTintColor
          if let letter = artwork.discipline?.first {
            glyphText = String(letter)
          }
        }
      }
    }
    

    很快,您将把这个类注册为Artwork注释的可重用注释视图。系统将一个注释作为newValue传递给它,你要做的是:

    • 1) 这些行执行与mapView(_:viewFor:)相同的操作,配置callout
    • 2) 然后设置标记的tint颜色,并将其pin图标或字形替换为注释discipline的第一个字母。

    2. Color My World

    现在切换到ViewController.swift。在调用loadInitialData()之前,将这行代码添加到viewDidLoad()

    mapView.register(
      ArtworkMarkerView.self,
      forAnnotationViewWithReuseIdentifier: 
        MKMapViewDefaultAnnotationViewReuseIdentifier)
    

    在这里,您使用地图视图的默认重用标识符注册新类。对于具有更多注释类型的应用程序,可以使用自定义标识符注册类。

    向下滚动扩展并删除mapView(_:viewFor:)

    构建和运行。然后移动地图,看看不同的颜色和标记标记:

    在地图的这一部分,有更多比地图视图显示的艺术。它通过将过于接近的标记聚类来减少混乱。在下一节中,您将看到所有注释。

    但是首先,设置字形的图像而不是它的文本。添加以下属性到Artwork.swift

    var image: UIImage {
      guard let name = discipline else { 
        return #imageLiteral(resourceName: "Flag") 
      }
    
      switch name {
      case "Monument":
        return #imageLiteral(resourceName: "Monument")
      case "Sculpture":
        return #imageLiteral(resourceName: "Sculpture")
      case "Plaque":
        return #imageLiteral(resourceName: "Plaque")
      case "Mural":
        return #imageLiteral(resourceName: "Mural")
      default:
        return #imageLiteral(resourceName: "Flag")
      }
    }
    

    这些来自icons8.com的图片已经在Assets.xcassets中了。

    然后,在ArtworkMarkerView.swift,将glyphText行替换为:

    glyphImage = artwork.image
    

    构建和运行看到不同的彩色标记与图像:

    这是另一个定制选项的segue你的下一个任务:用图像替换标记!

    3. Annotations with Images

    ArtworkViews.swift,添加以下类:

    class ArtworkView: MKAnnotationView {
      override var annotation: MKAnnotation? {
        willSet {
          guard let artwork = newValue as? Artwork else {
            return
          }
    
          canShowCallout = true
          calloutOffset = CGPoint(x: -5, y: 5)
          rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
    
          image = artwork.image
        }
      }
    }
    

    现在,您使用的是一个普通的旧MKAnnotationView,而不是MKMarkerAnnotationView,而且该视图有一个image属性。

    回到ViewController.swift中。在viewDidLoad()中,注册这个新类,而不是ArtworkMarkerView

    mapView.register(
      ArtworkView.self,
      forAnnotationViewWithReuseIdentifier: 
        MKMapViewDefaultAnnotationViewReuseIdentifier)
    

    构建并运行以看见所有的图标

    4. Custom Callout Accessory Views

    右边的callout附件是一个信息按钮,但是点击它会打开Maps。现在,您将更改按钮以显示Maps图标。

    ArtworkView中找到这一行:

    rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
    

    用以下代码替换这一行:

    let mapsButton = UIButton(frame: CGRect(
      origin: CGPoint.zero, 
      size: CGSize(width: 48, height: 48)))
    mapsButton.setBackgroundImage(#imageLiteral(resourceName: "Map"), for: .normal)
    rightCalloutAccessoryView = mapsButton
    

    这里,你创建一个UIButton,设置它的背景图像为一个地图图标,也来自于Assets.xcassets中的icons8.com。然后将视图的右侧callout附件设置为此按钮。

    构建和运行。然后点击一个视图来查看新的Maps按钮:

    最后的定制是detail callout附件。这是一个单一的行,这是足够短的位置文本,但一些较长的位置值被截断如下:

    现在您需要一个多行标签。添加以下代码到ArtworkViewwillSet:

    let detailLabel = UILabel()
    detailLabel.numberOfLines = 0
    detailLabel.font = detailLabel.font.withSize(12)
    detailLabel.text = artwork.subtitle
    detailCalloutAccessoryView = detailLabel
    

    构建和运行。然后点击一个视图来查看完整的长位置文本。

    现在您已经了解了使用MapKit的基础知识,但是您还可以添加更多内容:地图显示自定义、地理编码、地理围栏、自定义地图覆盖等等。 MapKit documentationLocation and Maps Programming Guide是查找更多信息的好地方。

    还可以看看WWDC 2019 Session 236: What’s New in MapKit and MapKit JS,以发现更多的酷功能添加到iOS 13中。

    后记

    本篇主要介绍了地图特定区域放大和创建自定义地图annotations,感兴趣的给个赞或者关注~~~

    相关文章

      网友评论

        本文标题:MapKit框架详细解析(九) —— 地图特定区域放大和创建自定

        本文链接:https://www.haomeiwen.com/subject/zjgkwhtx.html