[osx] Offline pack manager in osxapp

Added an offline pack download manager to osxapp to demonstrate the use of MGLOfflineStorage with Cocoa Bindings. Like the Offline Packs view controller in iosapp, new packs are added based on the foremost map view’s current state. Double-clicking a complete offline pack opens a new map window to the pack’s bounds.
This commit is contained in:
Minh Nguyễn
2016-03-28 00:05:58 -07:00
committed by Minh Nguyễn
parent 8c8b058038
commit 78f93708d6
7 changed files with 374 additions and 4 deletions

View File

@@ -15,8 +15,8 @@ extern NSString * const MGLMapboxAccessTokenDefaultsKey;
@property (assign) double pendingZoomLevel;
@property (copy) MGLMapCamera *pendingCamera;
@property (assign) MGLCoordinateBounds pendingVisibleCoordinateBounds;
@property (copy) NSURL *pendingStyleURL;
@property (assign) MGLMapDebugMaskOptions pendingDebugMask;
@end

View File

@@ -7,8 +7,58 @@ NSString * const MGLLastMapCameraDefaultsKey = @"MGLLastMapCamera";
NSString * const MGLLastMapStyleURLDefaultsKey = @"MGLLastMapStyleURL";
NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
/**
Some convenience methods to make offline pack properties easier to bind to.
*/
@implementation MGLOfflinePack (Additions)
+ (NSSet *)keyPathsForValuesAffectingStateImage {
return [NSSet setWithObjects:@"state", nil];
}
- (NSImage *)stateImage {
switch (self.state) {
case MGLOfflinePackStateComplete:
return [NSImage imageNamed:@"NSMenuOnStateTemplate"];
case MGLOfflinePackStateActive:
return [NSImage imageNamed:@"NSFollowLinkFreestandingTemplate"];
default:
return nil;
}
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesCompleted {
return [NSSet setWithObjects:@"progress", nil];
}
- (uint64_t)countOfResourcesCompleted {
return self.progress.countOfResourcesCompleted;
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesExpected {
return [NSSet setWithObjects:@"progress", nil];
}
- (uint64_t)countOfResourcesExpected {
return self.progress.countOfResourcesExpected;
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfBytesCompleted {
return [NSSet setWithObjects:@"progress", nil];
}
- (uint64_t)countOfBytesCompleted {
return self.progress.countOfBytesCompleted;
}
@end
@interface AppDelegate ()
@property (weak) IBOutlet NSArrayController *offlinePacksArrayController;
@end
@implementation AppDelegate
@@ -63,6 +113,8 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
[alert runModal];
[self showPreferences:nil];
}
[self.offlinePacksArrayController bind:@"content" toObject:[MGLOfflineStorage sharedOfflineStorage] withKeyPath:@"packs" options:nil];
}
- (void)applicationWillTerminate:(NSNotification *)notification {
@@ -79,6 +131,8 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
[[NSUserDefaults standardUserDefaults] setInteger:mapView.debugMask forKey:MGLLastMapDebugMaskDefaultsKey];
}
}
[self.offlinePacksArrayController unbind:@"content"];
}
#pragma mark Services
@@ -121,6 +175,45 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:NULL];
}
#pragma mark Offline pack management
- (IBAction)delete:(id)sender {
for (MGLOfflinePack *pack in self.offlinePacksArrayController.selectedObjects) {
[[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
[[NSAlert alertWithError:error] runModal];
}
}];
}
}
- (IBAction)chooseOfflinePack:(id)sender {
for (MGLOfflinePack *pack in self.offlinePacksArrayController.selectedObjects) {
switch (pack.state) {
case MGLOfflinePackStateComplete:
{
if ([pack.region isKindOfClass:[MGLTilePyramidOfflineRegion class]]) {
MGLTilePyramidOfflineRegion *region = (MGLTilePyramidOfflineRegion *)pack.region;
self.pendingVisibleCoordinateBounds = region.bounds;
[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:NULL];
}
break;
}
case MGLOfflinePackStateInactive:
[pack resume];
break;
case MGLOfflinePackStateActive:
[pack suspend];
break;
default:
break;
}
}
}
#pragma mark Help methods
- (IBAction)showShortcuts:(id)sender {
@@ -154,6 +247,9 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
if (menuItem.action == @selector(showPreferences:)) {
return YES;
}
if (menuItem.action == @selector(delete:)) {
return self.offlinePacksArrayController.selectedObjects.count;
}
return NO;
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10109" systemVersion="15E39d" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10109"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -17,6 +17,7 @@
</customObject>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
<outlet property="offlinePacksArrayController" destination="dWe-R6-sRz" id="Ar5-xu-ABm"/>
<outlet property="preferencesWindow" destination="UWc-yQ-qda" id="Ota-aT-Mz2"/>
</connections>
</customObject>
@@ -113,6 +114,12 @@
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
<menuItem title="Save Offline Pack…" id="UXB-sj-C7i">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="addOfflinePack:" target="-1" id="Usu-xO-QEx"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -516,6 +523,13 @@
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="Uix-g7-fAt"/>
<menuItem title="Offline Packs" id="YW3-jR-knj">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeKeyAndOrderFront:" target="Jjv-gs-Tx6" id="cJK-pv-fl5"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -551,7 +565,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="131" width="350" height="62"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1050"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="62"/>
<autoresizingMask key="autoresizingMask"/>
@@ -609,8 +623,190 @@
<point key="canvasLocation" x="754" y="210"/>
</window>
<userDefaultsController representsSharedInstance="YES" id="45S-yT-WUN"/>
<window title="Offline Packs" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="MBXOfflinePacksPanel" animationBehavior="default" id="Jjv-gs-Tx6" customClass="NSPanel">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="830" y="430" width="400" height="300"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" id="8ha-hw-zOD">
<rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q8b-0e-dLv">
<rect key="frame" x="-1" y="20" width="402" height="281"/>
<clipView key="contentView" id="J9U-Yx-o2S">
<rect key="frame" x="1" y="0.0" width="400" height="280"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
<rect key="frame" x="0.0" y="0.0" width="400" height="257"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="" editable="NO" width="16" minWidth="10" maxWidth="3.4028234663852886e+38" id="xtw-hQ-8C5" userLabel="State">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<imageCell key="dataCell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="edU-Yw-20f"/>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.stateImage" id="2wd-1J-TZt"/>
</connections>
</tableColumn>
<tableColumn editable="NO" width="116" minWidth="40" maxWidth="1000" id="2hD-LN-h0L">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Name">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="oys-QZ-34I">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.context" id="NtD-s5-ZUq">
<dictionary key="options">
<string key="NSValueTransformerName">OfflinePackNameValueTransformer</string>
</dictionary>
</binding>
</connections>
</tableColumn>
<tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="WfC-qb-HsW">
<numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="sNm-Qn-ne6"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesCompleted" id="mu6-Jg-GiU"/>
</connections>
</tableColumn>
<tableColumn identifier="" editable="NO" width="50" minWidth="10" maxWidth="3.4028234663852886e+38" id="Rrd-A9-jqc">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Total">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="mHy-qJ-rOA">
<numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="kyx-ZP-OBH"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesExpected" id="mh2-k0-vvB"/>
</connections>
</tableColumn>
<tableColumn identifier="" editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="h7m-6l-KaS">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Size">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="701-bg-k6L">
<byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" id="IXV-J9-sP3"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<connections>
<binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfBytesCompleted" id="Zsa-Na-yFN"/>
</connections>
</tableColumn>
</tableColumns>
<connections>
<action trigger="doubleAction" selector="chooseOfflinePack:" target="-1" id="pUN-eT-zRT"/>
</connections>
</tableView>
</subviews>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</clipView>
<scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
<rect key="frame" x="1" y="7" width="0.0" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" id="MAZ-Iq-hBi">
<rect key="frame" x="0.0" y="0.0" width="400" height="23"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wzf-ce-Spm">
<rect key="frame" x="0.0" y="-1" width="21" height="21"/>
<constraints>
<constraint firstAttribute="width" constant="21" id="5ST-tY-8Ph"/>
</constraints>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="sew-F7-i5T">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="addOfflinePack:" target="-1" id="SN0-PM-HoU"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7L7-hr-zId">
<rect key="frame" x="20" y="0.0" width="21" height="19"/>
<constraints>
<constraint firstAttribute="width" constant="21" id="JYb-AF-8gZ"/>
</constraints>
<buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="oTF-3m-6qT">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
CA
</string>
</buttonCell>
<connections>
<action selector="delete:" target="-1" id="EGL-bf-yUD"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="7L7-hr-zId" firstAttribute="centerY" secondItem="wzf-ce-Spm" secondAttribute="centerY" id="7TI-6w-bf1"/>
<constraint firstAttribute="bottom" secondItem="Q8b-0e-dLv" secondAttribute="bottom" constant="20" symbolic="YES" id="DZa-ly-bhV"/>
<constraint firstItem="wzf-ce-Spm" firstAttribute="top" secondItem="Q8b-0e-dLv" secondAttribute="bottom" id="LhK-5z-CQA"/>
<constraint firstItem="Q8b-0e-dLv" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="-1" id="Oyo-ch-rZo"/>
<constraint firstAttribute="bottom" secondItem="7L7-hr-zId" secondAttribute="bottom" id="TtY-j1-T5h"/>
<constraint firstItem="Q8b-0e-dLv" firstAttribute="top" secondItem="8ha-hw-zOD" secondAttribute="top" constant="-1" id="WDk-Ig-Grr"/>
<constraint firstAttribute="trailing" secondItem="Q8b-0e-dLv" secondAttribute="trailing" constant="-1" id="hHf-rd-Wcv"/>
<constraint firstItem="7L7-hr-zId" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="20" symbolic="YES" id="iKJ-ph-ACS"/>
<constraint firstAttribute="bottom" secondItem="wzf-ce-Spm" secondAttribute="bottom" constant="-1" id="jFV-Xi-fWr"/>
<constraint firstItem="wzf-ce-Spm" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" id="kJt-oJ-72R"/>
</constraints>
</view>
<point key="canvasLocation" x="720" y="317"/>
</window>
<arrayController objectClassName="MGLOfflinePack" editable="NO" avoidsEmptySelection="NO" id="dWe-R6-sRz" userLabel="Offline Packs Array Controller">
<declaredKeys>
<string>context</string>
<string>countOfResourcesCompleted</string>
<string>countOfResourcesExpected</string>
<string>countOfBytesCompleted</string>
<string>stateImage</string>
</declaredKeys>
</arrayController>
</objects>
<resources>
<image name="NSAddTemplate" width="11" height="11"/>
<image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
<image name="NSRemoveTemplate" width="11" height="11"/>
</resources>
</document>

View File

@@ -204,6 +204,10 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
appDelegate.pendingZoomLevel = -1;
appDelegate.pendingCamera = nil;
}
if (!MGLCoordinateBoundsIsEmpty(appDelegate.pendingVisibleCoordinateBounds)) {
self.mapView.visibleCoordinateBounds = appDelegate.pendingVisibleCoordinateBounds;
appDelegate.pendingVisibleCoordinateBounds = (MGLCoordinateBounds){ { 0, 0 }, { 0, 0 } };
}
if (appDelegate.pendingDebugMask) {
self.mapView.debugMask = appDelegate.pendingDebugMask;
}
@@ -350,6 +354,36 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[self.mapView addAnnotation:line];
}
#pragma mark Offline packs
- (IBAction)addOfflinePack:(id)sender {
NSAlert *namePrompt = [[NSAlert alloc] init];
namePrompt.messageText = @"Add offline pack";
namePrompt.informativeText = @"Choose a name for the pack:";
NSTextField *nameTextField = [[NSTextField alloc] initWithFrame:NSZeroRect];
nameTextField.placeholderString = MGLStringFromCoordinateBounds(self.mapView.visibleCoordinateBounds);
[nameTextField sizeToFit];
NSRect textFieldFrame = nameTextField.frame;
textFieldFrame.size.width = 300;
nameTextField.frame = textFieldFrame;
namePrompt.accessoryView = nameTextField;
[namePrompt addButtonWithTitle:@"Add"];
[namePrompt addButtonWithTitle:@"Cancel"];
if ([namePrompt runModal] != NSAlertFirstButtonReturn) {
return;
}
id <MGLOfflineRegion> region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:self.mapView.styleURL bounds:self.mapView.visibleCoordinateBounds fromZoomLevel:self.mapView.zoomLevel toZoomLevel:self.mapView.maximumZoomLevel];
NSData *context = [[NSValueTransformer valueTransformerForName:@"OfflinePackNameValueTransformer"] reverseTransformedValue:nameTextField.stringValue];
[[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable pack, NSError * _Nullable error) {
if (error) {
[[NSAlert alertWithError:error] runModal];
} else {
[pack resume];
}
}];
}
#pragma mark Help methods
- (IBAction)giveFeedback:(id)sender {
@@ -500,6 +534,10 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
if (menuItem.action == @selector(drawPolygonAndPolyLineAnnotations:)) {
return !_isShowingPolygonAndPolylineAnnotations;
}
if (menuItem.action == @selector(addOfflinePack:)) {
NSURL *styleURL = self.mapView.styleURL;
return !styleURL.isFileURL;
}
if (menuItem.action == @selector(giveFeedback:)) {
return YES;
}

View File

@@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface OfflinePackNameValueTransformer : NSValueTransformer
@end

View File

@@ -0,0 +1,33 @@
#import "OfflinePackNameValueTransformer.h"
static NSString * const MBXOfflinePackContextNameKey = @"Name";
@implementation OfflinePackNameValueTransformer
+ (Class)transformedValueClass {
return [NSString class];
}
+ (BOOL)allowsReverseTransformation {
return YES;
}
- (NSString *)transformedValue:(NSData *)context {
NSAssert([context isKindOfClass:[NSData class]], @"Context should be NSData.");
NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:context];
NSAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary.");
NSString *name = userInfo[MBXOfflinePackContextNameKey];
NSAssert([name isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string.");
return name;
}
- (NSData *)reverseTransformedValue:(NSString *)name {
NSAssert([name isKindOfClass:[NSString class]], @"Name should be a string.");
return [NSKeyedArchiver archivedDataWithRootObject:@{
MBXOfflinePackContextNameKey: name,
}];
}
@end

View File

@@ -29,6 +29,8 @@
'./LocationCoordinate2DTransformer.m',
'./MapDocument.h',
'./MapDocument.m',
'./OfflinePackNameValueTransformer.h',
'./OfflinePackNameValueTransformer.m',
'./TimeIntervalTransformer.h',
'./TimeIntervalTransformer.m',
'./NSValue+Additions.h',