Reputation: 211
Quick question for you. I have an app that I'm working on that will require the use of maps, but will not have a network connection. I have seen others ask similar questions about this, but my requirements are a slight bit different, so I wanted to post a new question.
Here are my requirements for the app.
So, is there any framework or method of caching map tiles that anyone can suggest?
Thanks go out in advance.
Upvotes: 13
Views: 16526
Reputation: 149
I used default map from MapKit and a subclass of MKTileOverlay to be able to save downloaded tiles and return already cached tiles without downloading them.
1) Change source for your default map from MapKit and use a subclass of MKTileOverlay (Used "open street map" here)
- (void)viewDidLoad{
[super viewDidLoad];
static NSString * const template = @"http://tile.openstreetmap.org/{z}/{x}/{y}.png";
VHTileOverlay *overlay = [[VHTileOverlay alloc] initWithURLTemplate:template];
overlay.canReplaceMapContent = YES;
[self.mapView addOverlay:overlay level:MKOverlayLevelAboveLabels];
}
2) subclass from MKTileOverlay
@interface VHTileOverlay() // MKTileOverlay subclass
@property (nonatomic, strong) NSOperationQueue *operationQueue;
@end
@implementation VHTileOverlay
-(instancetype)initWithURLTemplate:(NSString *)URLTemplate{
self = [super initWithURLTemplate:URLTemplate];
if(self){
self.directoryPath = cachePath;
self.operationQueue = [NSOperationQueue new];
}
return self;
}
-(NSURL *)URLForTilePath:(MKTileOverlayPath)path {
return [NSURL URLWithString:[NSString stringWithFormat:@"http://tile.openstreetmap.org/%ld/%ld/%ld.png", (long)path.z, (long)path.x, (long)path.y]];
}
-(void)loadTileAtPath:(MKTileOverlayPath)path
result:(void (^)(NSData *data, NSError *error))result
{
if (!result) {
return;
}
NSString *pathToFilfe = [[self URLForTilePath:path] absoluteString];
pathToFilfe = [pathToFilfe stringByReplacingOccurrencesOfString:@"/" withString:@"|"];
// @"/" - those are not approriate for file's url...
NSData *cachedData = [self loadFileWithName:pathToFilfe];
if (cachedData) {
result(cachedData, nil);
} else {
NSURLRequest *request = [NSURLRequest requestWithURL:[self URLForTilePath:path]];
__block VHTileOverlay *weakSelf = self;
[NSURLConnection sendAsynchronousRequest:request
queue:self.operationQueue
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
NSLog(@"%@",[weakSelf URLForTilePath:path]);
if(data){
[self saveFileWithName:[[weakSelf URLForTilePath:path] absoluteString] imageData:data];
}
result(data, connectionError);
}];
}
}
-(NSString *)pathToImageWithName:(NSString *)fileName
{
NSString *imageFilePath = [[OfflineMapCache sharedObject].cachePath stringByAppendingPathComponent:fileName];
return imageFilePath;
}
- (NSData *)loadFileWithName:(NSString *)fileName
{
NSString *imagePath = [self pathToImageWithName:fileName];
NSData *data = [[NSData alloc] initWithContentsOfFile:imagePath];
return data;
}
- (void)saveFileWithName:(NSString *)fileName imageData:(NSData *)imageData
{
// fileName = [fileName stringByReplacingOccurrencesOfString:@"/" withString:@"|"];
// NSString *imagePath = [self pathToImageWithName:fileName];
// [imageData writeToFile:imagePath atomically:YES];
}
Uncomment "saveFileWithName" and run it on simulator. You can also add NSLog(fileName) to know where to get all tiles you need. (Simulator cache is in Users/YOU/Library/Developer/CoreSimulator/Devices/... And Library is a hidden directory)
After you cached all you need just put in your app's bundle (just like an any other image, if you want from box cached map). And tell your
- (void)loadTileAtPath:(MKTileOverlayPath)path
result:(void (^)(NSData *data, NSError *error))result
to get tiles from bundle.
So now I can install my app, turn wi-fi off and I'll get those maps anyway.
Upvotes: 10
Reputation: 11409
See the skobbler/Telenav SDK - it's based on OpenStreetMap and is an (almost) full stack replacement to mapkit (map rendering, routing & turn by turn navigation) with integrated support for offline maps
Upvotes: 1
Reputation: 89
Unfortunately MapKit Framework doesn't support Offline Map Access. You Should prefer MapBox iOS SDK (MapBox iOS SDK is a toolset for building maps applications which support offline caching policy, zooming limits, retina display behavior, starting coordinate, and map view dragging deceleration etc....
Happy Coding
Upvotes: 1
Reputation: 2584
Try using http://mapbox.com/ which has both an iOS SDK and an app for constructing offline maps.
Upvotes: 3
Reputation: 124997
Check section 10.3 of the Google Maps Terms of Service and you'll find that you're not allowed to store any Google Maps content. That means that you'll need to provide not only your own map, but also replace the handy MapKit functionality. You really can't use MapKit at all for offline applications, as far as I can tell.
Upvotes: 1