如何正确使用mapbox的新MGLOfflinePackDelegate?

时间:2016-03-16 17:55:22

标签: ios swift mapbox offline

我正在创建一个需要离线地图的应用。我正在使用MapBox进行测试,MapBox从今天起支持离线地图(耶!)。我现在的代码似乎可以用于下载地图,但是报告进度的代表永远不会触发,我也不知道为什么会这样。

我有这个类用于我的mapView:

import UIKit
import Mapbox

class MapController: UIViewController, MGLMapViewDelegate,  UIPopoverPresentationControllerDelegate {

    @IBOutlet var mapView: MGLMapView!

    override func viewDidLoad() {
        super.viewDidLoad()       
        downloadIfNeeded()      
        mapView.maximumZoomLevel = 18
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func downloadIfNeeded() {
        MGLOfflineStorage.sharedOfflineStorage().getPacksWithCompletionHandler { (packs, error) in guard error == nil else {
                return
            }
            for pack in packs {
                let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
                if userInfo["name"] == "London" {
                    // allready downloaded
                    return
                }
            }

            // define the download region
            let sw = CLLocationCoordinate2DMake(51.212120, 4.415906)
            let ne = CLLocationCoordinate2DMake(51.223781, 4.442401)

            let bounds = MGLCoordinateBounds(sw: sw, ne: ne)
            let region = MGLTilePyramidOfflineRegion(styleURL: MGLStyle.streetsStyleURL(), bounds: bounds, fromZoomLevel: 10, toZoomLevel: 12)

            let userInfo = ["name": "London"]
            let context = NSKeyedArchiver.archivedDataWithRootObject(userInfo)

            MGLOfflineStorage.sharedOfflineStorage().addPackForRegion(region, withContext: context) { (pack, error) in
                guard error == nil else {
                    return
                }

                // create popup window with delegate
                let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
                let downloadProgress: MapDownloadController = storyboard.instantiateViewControllerWithIdentifier("MapDownloadController") as! MapDownloadController
                downloadProgress.modalPresentationStyle = .Popover
                downloadProgress.preferredContentSize = CGSizeMake(300, 150)

                let popoverMapDownloadController = downloadProgress.popoverPresentationController
                popoverMapDownloadController?.permittedArrowDirections = .Any
                popoverMapDownloadController?.delegate = self
                popoverMapDownloadController?.sourceView = self.mapView
                popoverMapDownloadController?.sourceRect = CGRect(x: self.mapView.frame.midX, y: self.mapView.frame.midY, width: 1, height: 1)
                self.presentViewController(downloadProgress, animated: true, completion: nil)

                // set popup as delegate <----
                pack!.delegate = downloadProgress

                // start downloading
                pack!.resume()
            }
        }
    }
}

MapDownloadController是一个显示为弹出窗口的View(参见上面的代码),并且有一个MGLOfflinePackDelegate:

import UIKit
import Mapbox

class MapDownloadController: UIViewController, MGLOfflinePackDelegate {

    @IBOutlet var progress: UIProgressView!
    @IBOutlet var progressText: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func offlinePack(pack: MGLOfflinePack, progressDidChange progress: MGLOfflinePackProgress) {
        // this function is never called, but why? <----
        let completed = progress.countOfResourcesCompleted
        let expected = progress.countOfResourcesExpected
        let bytes = progress.countOfBytesCompleted

        let MB = bytes / 1024

        let str: String = "\(completed)/\(expected) voltooid (\(MB)MB)"
        progressText.text = str

        self.progress.setProgress(Float(completed) / Float(expected), animated: true)
    }

    func offlinePack(pack: MGLOfflinePack, didReceiveError error: NSError) {
        // neither is this one... <----
        let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
        let strError = error.localizedFailureReason
    }

    func offlinePack(pack: MGLOfflinePack, didReceiveMaximumAllowedMapboxTiles maximumCount: UInt64) {
        // .. or this one  <----
        let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
    }
}

这几乎都是从文档中获取的,那么为什么代理的函数(func offlinePack)从未被调用过?我测试了断点,所以我确信它不是。仍然会显示弹出窗口并下载该区域。 (通过观察网络流量和列出脱机包的其他代码进行检查。)

2 个答案:

答案 0 :(得分:2)

这是使用Minh’s answercurrent v3.2.0b1 example code的极其简单的实现。期待这个答案很快就会过时,因为我们仍然在开发v3.2.0版本。

import UIKit
import Mapbox

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate, MGLOfflinePackDelegate {

    @IBOutlet var mapView: MGLMapView!

    // Array of offline packs for the delegate work around (and your UI, potentially)
    var offlinePacks = [MGLOfflinePack]()

    override func viewDidLoad() {
        super.viewDidLoad()

        mapView.maximumZoomLevel = 2
        downloadOffline()
    }

    func downloadOffline() {
        // Create a region that includes the current viewport and any tiles needed to view it when zoomed further in.
        let region = MGLTilePyramidOfflineRegion(styleURL: mapView.styleURL, bounds: mapView.visibleCoordinateBounds, fromZoomLevel: mapView.zoomLevel, toZoomLevel: mapView.maximumZoomLevel)

        // Store some data for identification purposes alongside the downloaded resources.
        let userInfo = ["name": "My Offline Pack"]
        let context = NSKeyedArchiver.archivedDataWithRootObject(userInfo)

        // Create and register an offline pack with the shared offline storage object.
        MGLOfflineStorage.sharedOfflineStorage().addPackForRegion(region, withContext: context) { (pack, error) in
            guard error == nil else {
                print("The pack couldn’t be created for some reason.")
                return
            }

            // Set the pack’s delegate (assuming self conforms to the MGLOfflinePackDelegate protocol).
            pack!.delegate = self

            // Start downloading.
            pack!.resume()

            // Retain reference to pack to work around it being lost and not sending delegate messages
            self.offlinePacks.append(pack!)
        }
    }

    func offlinePack(pack: MGLOfflinePack, progressDidChange progress: MGLOfflinePackProgress) {
        let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
        let completed = progress.countOfResourcesCompleted
        let expected = progress.countOfResourcesExpected
        print("Offline pack “\(userInfo["name"])” has downloaded \(completed) of \(expected) resources.")
    }

    func offlinePack(pack: MGLOfflinePack, didReceiveError error: NSError) {
        let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
        print("Offline pack “\(userInfo["name"])” received error: \(error.localizedFailureReason)")
    }

    func offlinePack(pack: MGLOfflinePack, didReceiveMaximumAllowedMapboxTiles maximumCount: UInt64) {
        let userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as! [String: String]
        print("Offline pack “\(userInfo["name"])” reached limit of \(maximumCount) tiles.")
    }

}

答案 1 :(得分:1)

(从this GitHub issue交叉发布。)

这是a bug in the SDK。解决方法是让完成处理程序将传入的MGLOfflinePack对象分配给周围MapDownloadController类(example)中的ivar或其他强引用。