美文网首页iOS Developerswift 文章收集
iOS 9: Day by Day 第十天 MapKit Tra

iOS 9: Day by Day 第十天 MapKit Tra

作者: 戴维营教育 | 来源:发表于2016-04-21 09:42 被阅读244次

    本文翻译自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职业教育!紧跟时代潮流,不弄虚作假!不忘初心!

    相关文章

      网友评论

        本文标题:iOS 9: Day by Day 第十天 MapKit Tra

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