Skip to content
This repository has been archived by the owner on Jun 14, 2024. It is now read-only.

Add single (“simple”) offline pack example [WIP] #12

Merged
merged 7 commits into from
Apr 4, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Examples.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
96115A681CAD4E1C000963B8 /* OfflinePackExample.m in Sources */ = {isa = PBXBuildFile; fileRef = 96115A671CAD4E1C000963B8 /* OfflinePackExample.m */; };
96115A6A1CAD5012000963B8 /* OfflinePackExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96115A691CAD5012000963B8 /* OfflinePackExample.swift */; };
961962911C581700002D3DAB /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 961962901C581700002D3DAB /* main.m */; };
961962941C581700002D3DAB /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 961962931C581700002D3DAB /* AppDelegate.m */; };
9619629C1C581700002D3DAB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9619629B1C581700002D3DAB /* Assets.xcassets */; };
Expand Down Expand Up @@ -83,6 +85,9 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
96115A661CAD4E1C000963B8 /* OfflinePackExample.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OfflinePackExample.h; sourceTree = "<group>"; };
96115A671CAD4E1C000963B8 /* OfflinePackExample.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OfflinePackExample.m; sourceTree = "<group>"; };
96115A691CAD5012000963B8 /* OfflinePackExample.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OfflinePackExample.swift; path = Swift/OfflinePackExample.swift; sourceTree = "<group>"; };
9619628C1C581700002D3DAB /* Examples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Examples.app; sourceTree = BUILT_PRODUCTS_DIR; };
961962901C581700002D3DAB /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
961962921C581700002D3DAB /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -271,6 +276,7 @@
96D431FA1C84B4F7007D09D1 /* DrawingACustomMarkerExample.h */,
9682470D1C5BF12700494AB8 /* DrawingAMarkerExample.h */,
9682471E1C5C1C0400494AB8 /* DrawingAPolygonExample.h */,
96115A661CAD4E1C000963B8 /* OfflinePackExample.h */,
968247281C5C1FF800494AB8 /* PointConversionExample.h */,
968247061C5BEBDC00494AB8 /* SatelliteStyleExample.h */,
9691AAA51C5AAD8F006A58C6 /* SimpleMapViewExample.h */,
Expand All @@ -293,6 +299,7 @@
96D431FB1C84B4F7007D09D1 /* DrawingACustomMarkerExample.m */,
9682470E1C5BF12700494AB8 /* DrawingAMarkerExample.m */,
9682471F1C5C1C0400494AB8 /* DrawingAPolygonExample.m */,
96115A671CAD4E1C000963B8 /* OfflinePackExample.m */,
968247291C5C1FF800494AB8 /* PointConversionExample.m */,
968247071C5BEBDC00494AB8 /* SatelliteStyleExample.m */,
9691AAA61C5AAD8F006A58C6 /* SimpleMapViewExample.m */,
Expand All @@ -314,6 +321,7 @@
96D432031C84B62A007D09D1 /* DrawingACustomMarkerExample.swift */,
9682470B1C5BF08400494AB8 /* DrawingAMarkerExample.swift */,
968247211C5C1C9B00494AB8 /* DrawingAPolygonExample.swift */,
96115A691CAD5012000963B8 /* OfflinePackExample.swift */,
9682472B1C5C20F800494AB8 /* PointConversionExample.swift */,
968247091C5BEE4D00494AB8 /* SatelliteStyleExample.swift */,
9691AAAD1C5AB5A1006A58C6 /* SimpleMapViewExample.swift */,
Expand Down Expand Up @@ -480,8 +488,10 @@
9691AAA81C5AAD8F006A58C6 /* DefaultStylesExample.m in Sources */,
9682470A1C5BEE4D00494AB8 /* SatelliteStyleExample.swift in Sources */,
968247081C5BEBDC00494AB8 /* SatelliteStyleExample.m in Sources */,
96115A681CAD4E1C000963B8 /* OfflinePackExample.m in Sources */,
9682471A1C5C115000494AB8 /* DrawingAGeoJSONLineExample.m in Sources */,
968247051C5BDDC600494AB8 /* CustomRasterStyleExample.swift in Sources */,
96115A6A1CAD5012000963B8 /* OfflinePackExample.swift in Sources */,
968247241C5C1D9500494AB8 /* CameraAnimationExample.swift in Sources */,
968247151C5C102B00494AB8 /* CalloutDelegateUsageExample.swift in Sources */,
968247271C5C1DC700494AB8 /* CameraAnimationExample.m in Sources */,
Expand Down
1 change: 1 addition & 0 deletions Examples/Examples.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ extern NSString *const MBXExampleDrawingAGeoJSONLine;
extern NSString *const MBXExampleDrawingACustomMarker;
extern NSString *const MBXExampleDrawingAMarker;
extern NSString *const MBXExampleDrawingAPolygon;
extern NSString *const MBXExampleOfflinePack;
extern NSString *const MBXExamplePointConversion;
extern NSString *const MBXExampleSatelliteStyle;
extern NSString *const MBXExampleSimpleMapView;
Expand Down
3 changes: 2 additions & 1 deletion Examples/Examples.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ + (NSArray *)list {
MBXExampleDrawingACustomMarker,
MBXExampleDrawingAMarker,
MBXExampleDrawingAPolygon,
MBXExampleSatelliteStyle,
MBXExampleOfflinePack,
MBXExamplePointConversion,
MBXExampleSatelliteStyle,
MBXExampleSimpleMapView,
]];

Expand Down
5 changes: 5 additions & 0 deletions Examples/ExamplesTableViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ - (void)viewDidLoad {

// do this ourselves, as automatic doesn't work with fast swipes
self.clearsSelectionOnViewWillAppear = NO;

[self performSegueWithIdentifier:MBXSegueTableToExample sender:self];
}

-(void)viewWillAppear:(BOOL)animated {
Expand Down Expand Up @@ -65,6 +67,9 @@ - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

ExamplesContainerViewController *destinationVC = [segue destinationViewController];
destinationVC.exampleToLoad = senderCell.textLabel.text;
} else {
ExamplesContainerViewController *destinationVC = [segue destinationViewController];
destinationVC.exampleToLoad = MBXExampleOfflinePack;
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions Examples/ObjectiveC/OfflinePackExample.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// OfflinePackExample.h
// Examples
//
// Created by Jason Wray on 3/31/16.
// Copyright © 2016 Mapbox. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface OfflinePackExample : UIViewController

@end
123 changes: 123 additions & 0 deletions Examples/ObjectiveC/OfflinePackExample.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
//
// OfflinePackExample.m
// Examples
//
// Created by Jason Wray on 3/31/16.
// Copyright © 2016 Mapbox. All rights reserved.
//

#import "OfflinePackExample.h"
@import Mapbox;

NSString *const MBXExampleOfflinePack = @"OfflinePackExample";

@interface OfflinePackExample () <MGLMapViewDelegate>

@property (nonatomic) MGLMapView *mapView;
@property (nonatomic) UIProgressView *progressView;

@end

@implementation OfflinePackExample

- (void)viewDidLoad {
[super viewDidLoad];

self.mapView = [[MGLMapView alloc] initWithFrame:self.view.bounds styleURL:[MGLStyle darkStyleURL]];
self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.mapView.tintColor = [UIColor lightGrayColor];
self.mapView.delegate = self;
[self.view addSubview:self.mapView];

[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(22.27933, 114.16281)
zoomLevel:13
animated:NO];

// Setup offline pack notification handlers.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackProgressDidChange:) name:MGLOfflinePackProgressChangedNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidReceiveError:) name:MGLOfflinePackErrorNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(offlinePackDidReceiveMaximumAllowedMapboxTiles:) name:MGLOfflinePackMaximumMapboxTilesReachedNotification object:nil];
}

- (void)mapViewDidFinishLoadingMap:(MGLMapView *)mapView {
// Start downloading tiles and resources for z13-16.
[self startOfflinePackDownload];
}

- (void)dealloc {
// Remove offline pack observers.
[[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)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.
id <MGLOfflineRegion> region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:self.mapView.styleURL bounds:self.mapView.visibleCoordinateBounds fromZoomLevel:self.mapView.zoomLevel toZoomLevel:16];

// Store some data for identification purposes alongside the downloaded resources.
NSDictionary *userInfo = @{ @"name": @"My Offline Pack" };
NSData *context = [NSKeyedArchiver archivedDataWithRootObject:userInfo];

// Create and register an offline pack with the shared offline storage object.
[[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack *pack, NSError *error) {
if (error != nil) {
// The pack couldn’t be created for some reason.
NSLog(@"Error: %@", error.localizedFailureReason);
} else {
// Start downloading.
[pack resume];
}
}];
}

#pragma mark - MGLOfflinePack notification handlers

- (void)offlinePackProgressDidChange:(NSNotification *)notification {
MGLOfflinePack *pack = notification.object;

// Get the associated user info for the pack; in this case, `name = My Offline Pack`
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];

MGLOfflinePackProgress progress = pack.progress;
// or [notification.userInfo[MGLOfflinePackProgressUserInfoKey] MGLOfflinePackProgressValue]
uint64_t completedResources = progress.countOfResourcesCompleted;
uint64_t expectedResources = progress.countOfResourcesExpected;

// Calculate current progress percentage.
float progressPercentage = (float)completedResources / expectedResources;

// Setup the progress bar.
if (!self.progressView) {
self.progressView = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
CGSize frame = self.view.bounds.size;
self.progressView.frame = CGRectMake(frame.width / 4, frame.height * 0.75, frame.width / 2, 10);
[self.view addSubview:self.progressView];
}

[self.progressView setProgress:progressPercentage animated:YES];

// If this pack has finished, print its size and resource count.
if (completedResources == expectedResources) {
NSString *byteCount = [NSByteCountFormatter stringFromByteCount:progress.countOfBytesCompleted countStyle:NSByteCountFormatterCountStyleMemory];
NSLog(@"Offline pack “%@” completed: %@, %llu resources", userInfo[@"name"], byteCount, completedResources);
} else {
// Otherwise, print download/verification progress.
NSLog(@"Offline pack “%@” has %llu of %llu resources — %.2f%%.", userInfo[@"name"], completedResources, expectedResources, progressPercentage * 100);
}
}

- (void)offlinePackDidReceiveError:(NSNotification *)notification {
MGLOfflinePack *pack = notification.object;
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
NSError *error = notification.userInfo[MGLOfflinePackErrorUserInfoKey];
NSLog(@"Offline pack “%@” received error: %@", userInfo[@"name"], error.localizedFailureReason);
}

- (void)offlinePackDidReceiveMaximumAllowedMapboxTiles:(NSNotification *)notification {
MGLOfflinePack *pack = notification.object;
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:pack.context];
uint64_t maximumCount = [notification.userInfo[MGLOfflinePackMaximumCountUserInfoKey] unsignedLongLongValue];
NSLog(@"Offline pack “%@” reached limit of %llu tiles.", userInfo[@"name"], maximumCount);
}

@end
120 changes: 120 additions & 0 deletions Examples/Swift/OfflinePackExample.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//
// OfflinePackExample.swift
// Examples
//
// Created by Jason Wray on 3/31/16.
// Copyright © 2016 Mapbox. All rights reserved.
//

import Mapbox

@objc(OfflinePackExample_Swift)

class OfflinePackExample: 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 = .grayColor()
mapView.delegate = self
view.addSubview(mapView)

mapView.setCenterCoordinate(CLLocationCoordinate2DMake(22.27933, 114.16281),
zoomLevel: 13, animated: false)

// Setup offline pack notification handlers.
NSNotificationCenter.defaultCenter().addObserver(self, selector: "offlinePackProgressDidChange:", name: MGLOfflinePackProgressChangedNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "offlinePackDidReceiveError:", name: MGLOfflinePackProgressChangedNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "offlinePackDidReceiveMaximumAllowedMapboxTiles:", name: MGLOfflinePackProgressChangedNotification, object: nil)
}

func mapViewDidFinishLoadingMap(mapView: MGLMapView) {
// Start downloading tiles and resources for z13-16.
startOfflinePackDownload()
}

deinit {
// Remove offline pack observers.
NSNotificationCenter.defaultCenter().removeObserver(self)
}

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: 16)

// 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 {
// The pack couldn’t be created for some reason.
print("Error: \(error?.localizedFailureReason)")
return
}

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

// MARK: - MGLOfflinePack notification handlers

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,
userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as? [String: String] {
let progress = pack.progress
// or notification.userInfo![MGLOfflinePackProgressUserInfoKey]!.MGLOfflinePackProgressValue
let completedResources = progress.countOfResourcesCompleted
let 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.size
progressView.frame = CGRectMake(frame.width / 4, frame.height * 0.75, frame.width / 2, 10)
view.addSubview(progressView)
}

progressView.progress = progressPercentage

// If this pack has finished, print its size and resource count.
if completedResources == expectedResources {
let byteCount = NSByteCountFormatter.stringFromByteCount(Int64(pack.progress.countOfBytesCompleted), countStyle: NSByteCountFormatterCountStyle.Memory)
print("Offline pack “\(userInfo["name"])” completed: \(byteCount), \(completedResources) resources")
} else {
// Otherwise, print download/verification progress.
print("Offline pack “\(userInfo["name"])” has \(completedResources) of \(expectedResources) resources — \(progressPercentage * 100)%.")
}
}
}

func offlinePackDidReceiveError(notification: NSNotification) {
if let pack = notification.object as? MGLOfflinePack,
userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as? [String: String],
error = notification.userInfo?[MGLOfflinePackErrorUserInfoKey] as? NSError {
print("Offline pack “\(userInfo["name"])” received error: \(error.localizedFailureReason)")
}
}

func offlinePackDidReceiveMaximumAllowedMapboxTiles(notification: NSNotification) {
if let pack = notification.object as? MGLOfflinePack,
userInfo = NSKeyedUnarchiver.unarchiveObjectWithData(pack.context) as? [String: String],
maximumCount = notification.userInfo?[MGLOfflinePackMaximumCountUserInfoKey]?.unsignedLongLongValue {
print("Offline pack “\(userInfo["name"])” reached limit of \(maximumCount) tiles.")
}
}

}