iOS 9: Day by Day 第十天 MapKit Transit

本文翻译自Chris Grant的《iOS9 Day-by-Day :: Day 10 :: MapKit Transit》(https://www.shinobicontrols.com/blog/ios9-day-by-day-day10-mapkit-transit)。感谢Chris Grant的辛苦工作!

MapKit的每一次迭代都会引入一些新特性,iOS 9也不例外。在本文中,我们将了解一下iOS 9为我们准备的新API,并在应用中与新的ETA(预计到达时间)功能一起使用。

重要的新API

MapKit视图的改进

给地图的弹出视图增加了更多高级特性。MKAnnotation有一下属性可以进行定制:

  • 标题
  • 子标题
  • 右辅助视图
  • 左辅助视图
  • 详情弹出视图

iOS 9新增详情弹出视图,允许我们为标准的弹出视图指定详细信息。该视图完整支持自动布局和约束,可以用于自定义已有的弹出菜单。

下面是MKMapView的一些新增属性:

  • showsTraffic
  • showsScale
  • showsCompass

交通运输功能改进

iOS 9引入了一个新的MKDirectionsTransportType类型,名为MKDirectionsTransitType。该类型只能用于ETA请求。当我们使用calculateETAWithCompletionHandler请求一个ETA信息时,可以在完成时的处理块(Block)中获取到响应对象(MKETAResponse)。该对象包含了预计旅行时间、距离、预计到达时间以及预计起飞时间。

创建一个示例应用

为了演示这些API的用法,并且测试一下ETA请求功能。我们将创建以下APP,它能够显示从选中位置到伦敦的各地标建筑的交通信息。

首先在故事板(Storyboard)上设置一个MKMapView、一个UITableView以及它们所需的约束条件。在完成上面的事情后,给UITableView添加一个Cell原型。我们不会很深入的关注UI,只要确认ViewController是表格视图的UITableViewDataSource和地图的MKMapViewDelegate。整个界面看起来如下:

我们需要创建一个自定义的Cell。它拥有一些连接Storyboard上Cell原型上的IBOutlet属性。

class DestinationTableViewCell: UITableViewCell {

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var etaLabel: UILabel!
    @IBOutlet weak var departureTimeLabel: UILabel!
    @IBOutlet weak var arrivalTimeLabel: UILabel!
    @IBOutlet weak var distanceLabel: UILabel!

}

设置好Storyboard后,下面开始给地图添加大头针。创建一个“Destination”类来存储位置信息。

class Destination {

    let coordinate:CLLocationCoordinate2D
    private var addressDictionary:[String : AnyObject]
    let name:String

    init(withName placeName: String,
        latitude: CLLocationDegrees,
        longitude: CLLocationDegrees,
        address:[String:AnyObject])
    {
        name = placeName
        coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
        addressDictionary = address
    }
}

创建位置对象:

let stPauls = Destination(
    withName: "St Paul's Cathedral", 
    latitude: 51.5138244, 
    longitude: -0.0983483,
    address: [
        CNPostalAddressStreetKey:"St. Paul's Churchyard",
        CNPostalAddressCityKey:"London",
        CNPostalAddressPostalCodeKey:"EC4M 8AD",
        CNPostalAddressCountryKey:"England"])

创建多个位置对象,并将它们存储在一个数组中,方便后面在地图上进行显示。

ViewControllerviewDidLoad方法里添加一下代码来将目标地址在地图上标记出来。

for destination in destinations {
        let annotation = MKPointAnnotation()
        annotation.coordinate = destination.coordinate
        mapView.addAnnotation(annotation)
    }

我们同样可以设置地图的初始化显示区域。

mapView.region = MKCoordinateRegion(
    center: CLLocationCoordinate2D(
        latitude: CLLocationDegrees(51.5074157),
        longitude: CLLocationDegrees(-0.1201011)),
    span: MKCoordinateSpan(
        latitudeDelta: CLLocationDegrees(0.025),
        longitudeDelta: CLLocationDegrees(0.025)))

下一步,在表格上显示目标地址:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return destinations.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCellWithIdentifier("destinationCell") as! DestinationTableViewCell
    cell.destination = destinations[indexPath.row]
    return cell
}

运行程序就可以看到地图上标记了所有的目标地址,而表格里显示这写位置的地名。

现在我们还不能计算方向,因为还没有标记起始位置。虽然可以使用用户当前的位置,但理想的是让用户自己在地图上进行点击选择。下面给地图添加点击手势:

let tap = UITapGestureRecognizer(target: self, action: "handleTap:")
mapView.addGestureRecognizer(tap)

处理点击手势,并将视图坐标转换为地理坐标:

let point = gestureRecognizer.locationInView(mapView)  
userCoordinate = mapView.convertPoint(point, toCoordinateFromView:mapView)

一旦存储了用户的位置坐标后,添加一个大头针来表示,并且记得移除之前放置的大头针。

if userAnnotation != nil {
    mapView.removeAnnotation(userAnnotation!)
}

userAnnotation = MKPointAnnotation()
userAnnotation!.coordinate = userCoordinate!
mapView.addAnnotation(userAnnotation!)

最后,设置表格中的ETA信息。先从改变可视区域的Cell开始:

for cell in self.tableView.visibleCells as! [DestinationTableViewCell] {
    cell.userCoordinate = userCoordinate
}

为了防止表格滚动时更新Cell的内容,还需要改变tableView:cellForRowAtIndexPath方法。

cell.userCoordinate = userCoordinate

一旦Cell上的坐标发生改变,需要触发一次更新进行显示。

var userCoordinate:CLLocationCoordinate2D? {
    didSet {

        etaLabel.text = ""
        departureTimeLabel.text = "Departure Time:"
        arrivalTimeLabel.text = "Arrival Time:"
        distanceLabel.text = "Distance:"

        guard let coordinate = userCoordinate else { return }

既然我们知道了用户坐标以及起始位置,就可以创建一个MKDirectionsRequest对象来计算ETA信息。使用位置坐标初始化MKMapItem对象,并设置起点和终点属性。将方向类型的属性设置为.Transit。最后调用calculateETAWithCompletionHandler来请求ETA信息。

let request = MKDirectionsRequest()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: coordinate, addressDictionary: nil))
request.destination = destination!.mapItem
request.transportType = .Transit
let directions = MKDirections(request: request)
directions.calculateETAWithCompletionHandler { response, error -> Void in
    if let err = error {
        self.etaLabel.text = err.userInfo["NSLocalizedFailureReason"] as? String
               return
    }

    self.etaLabel.text = "\(response!.expectedTravelTime/60) minutes travel time"
    self.departureTimeLabel.text = "Departure Time: \(response!.expectedDepartureDate)"
    self.arrivalTimeLabel.text = "Arrival Time: \(response!.expectedArrivalDate)"
    self.distanceLabel.text = "Distance: \(response!.distance) meters"
}

运行程序,结果如下:

点击地图后就会更新Cell中的ETA信息。最后一件事就是修改“View Route”按钮的动作处理方法,给它们添加一下代码:

guard let mapDestination = destination else { return }

let launchOptions = [MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeTransit]
mapDestination.mapItem.openInMapsWithLaunchOptions(launchOptions)

点击后就会打开地图应用,并显示从当前位置开始的路线图。

自定义大头针的颜色

上面应用的功能虽然完整,但是不能区分哪个是目标位置,哪个是用户。我们可以通过实现MKMapViewDelegate来改变大头针的外观。

func mapView(mapView: MKMapView, viewForAnnotation annotation: MKAnnotation) -> MKAnnotationView? {
    let pin = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
    pin.pinTintColor = annotation === userAnnotation ? UIColor.redColor() : UIColor.blueColor()
    return pin
}

pinTintColor是iOS 9引入的新属性,它运行我们设置每个大头针顶部的颜色。如上所示,一旦传递给mapView:viewForAnnotationannotation对象是用户的位置,我们就可以将它设置为红色,而其它设置为蓝色。这样就可以区分目标位置和用户所在的位置了。

更多越多

更多关于MapKit的信息可以观看WWDC session 206(What's New in MapKit)。别忘了我们可以在GitHub上下载到本文的示例代码。

戴维营教育

戴维营教育(Dive In Education),潜心做IT职业教育!紧跟时代潮流,不弄虚作假!不忘初心!

  • 官网:戴维营教育http://www.diveinedu.com
  • 在线视频:戴维营学院http://v.diveinedu.com
  • 问答网:潜心俱乐部http://divein.club

你可能感兴趣的:(iOS 9: Day by Day 第十天 MapKit Transit)