Download an offline map
See our mobile offline help page for more information about uses and limitations.
For a more comprehensive example that manages offline packs, see MBXOfflinePacksTableViewController
.
ViewController
import Mapbox class ViewController: UIViewController, MGLMapViewDelegate {var mapView: MGLMapView!var progressView: UIProgressView! override func viewDidLoad() {super.viewDidLoad() mapView = MGLMapView(frame: view.bounds, styleURL: MGLStyle.darkStyleURL)mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]mapView.tintColor = .graymapView.delegate = selfview.addSubview(mapView) mapView.setCenter(CLLocationCoordinate2D(latitude: 22.27933, longitude: 114.16281),zoomLevel: 13, animated: false) // Setup offline pack notification handlers.NotificationCenter.default.addObserver(self, selector: #selector(offlinePackProgressDidChange), name: NSNotification.Name.MGLOfflinePackProgressChanged, object: nil)NotificationCenter.default.addObserver(self, selector: #selector(offlinePackDidReceiveError), name: NSNotification.Name.MGLOfflinePackError, object: nil)NotificationCenter.default.addObserver(self, selector: #selector(offlinePackDidReceiveMaximumAllowedMapboxTiles), name: NSNotification.Name.MGLOfflinePackMaximumMapboxTilesReached, object: nil)} func mapViewDidFinishLoadingMap(_ mapView: MGLMapView) {// Start downloading tiles and resources for z13-14.startOfflinePackDownload()} override func viewDidDisappear(_ animated: Bool) {super.viewDidDisappear(animated) // When leaving this view controller, suspend offline downloads.guard let packs = MGLOfflineStorage.shared.packs else { return }for pack in packs {if let userInfo = NSKeyedUnarchiver.unarchiveObject(with: pack.context) as? [String: String] {print("Suspending download of offline pack: “\(userInfo["name"] ?? "unknown")”")}pack.suspend()}} func startOfflinePackDownload() {// Create a region that includes the current viewport and any tiles needed to view it when zoomed further in.// Because tile count grows exponentially with the maximum zoom level, you should be conservative with your `toZoomLevel` setting.let region = MGLTilePyramidOfflineRegion(styleURL: mapView.styleURL, bounds: mapView.visibleCoordinateBounds, fromZoomLevel: mapView.zoomLevel, toZoomLevel: 14) // Store some data for identification purposes alongside the downloaded resources.let userInfo = ["name": "My Offline Pack"]let context = NSKeyedArchiver.archivedData(withRootObject: userInfo) // Create and register an offline pack with the shared offline storage object. MGLOfflineStorage.shared.addPack(for: region, withContext: context) { (pack, error) inguard error == nil else {// The pack couldn’t be created for some reason.print("Error: \(error?.localizedDescription ?? "unknown error")")return} // Start downloading.pack!.resume()} } // MARK: - MGLOfflinePack notification handlers @objc func offlinePackProgressDidChange(notification: NSNotification) {// Get the offline pack this notification is regarding,// and the associated user info for the pack; in this case, `name = My Offline Pack`if let pack = notification.object as? MGLOfflinePack,let userInfo = NSKeyedUnarchiver.unarchiveObject(with: pack.context) as? [String: String] {let progress = pack.progress// or notification.userInfo![MGLOfflinePackProgressUserInfoKey]!.MGLOfflinePackProgressValuelet completedResources = progress.countOfResourcesCompletedlet expectedResources = progress.countOfResourcesExpected // Calculate current progress percentage.let progressPercentage = Float(completedResources) / Float(expectedResources) // Setup the progress bar.if progressView == nil {progressView = UIProgressView(progressViewStyle: .default)let frame = view.bounds.sizeprogressView.frame = CGRect(x: frame.width / 4, y: frame.height * 0.75, width: frame.width / 2, height: 10)view.addSubview(progressView)} progressView.progress = progressPercentage // If this pack has finished, print its size and resource count.if completedResources == expectedResources {let byteCount = ByteCountFormatter.string(fromByteCount: Int64(pack.progress.countOfBytesCompleted), countStyle: ByteCountFormatter.CountStyle.memory)print("Offline pack “\(userInfo["name"] ?? "unknown")” completed: \(byteCount), \(completedResources) resources")} else {// Otherwise, print download/verification progress.print("Offline pack “\(userInfo["name"] ?? "unknown")” has \(completedResources) of \(expectedResources) resources — \(String(format: "%.2f", progressPercentage * 100))%.")}}} @objc func offlinePackDidReceiveError(notification: NSNotification) {if let pack = notification.object as? MGLOfflinePack,let userInfo = NSKeyedUnarchiver.unarchiveObject(with: pack.context) as? [String: String],let error = notification.userInfo?[MGLOfflinePackUserInfoKey.error] as? NSError {print("Offline pack “\(userInfo["name"] ?? "unknown")” received error: \(error.localizedFailureReason ?? "unknown error")")}} @objc func offlinePackDidReceiveMaximumAllowedMapboxTiles(notification: NSNotification) {if let pack = notification.object as? MGLOfflinePack,let userInfo = NSKeyedUnarchiver.unarchiveObject(with: pack.context) as? [String: String],let maximumCount = (notification.userInfo?[MGLOfflinePackUserInfoKey.maximumCount] as AnyObject).uint64Value {print("Offline pack “\(userInfo["name"] ?? "unknown")” reached limit of \(maximumCount) tiles.")}} }