Commit 58e6a378 authored by Deployer's avatar Deployer

Updated Mapbox to version 3.6

parent 6dc5cb25
# Created by https://www.gitignore.io/api/swift,xcode,carthage,cocoapods,objective-c
### Carthage ###
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
### CocoaPods ###
## CocoaPods GitIgnore Template
# CocoaPods - Only use to conserve bandwidth / Save time on Pushing
# - Also handy if you have a lage number of dependant pods
# - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGONRE THE LOCK FILE
Pods/
### Objective-C ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# CocoaPods - Refactored to standalone file
.bundle
# Carthage - Refactored to standalone file
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode
iOSInjectionProject/
### Objective-C Patch ###
### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
## Various settings
## Other
## Obj-C/Swift specific
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
.build/
# CocoaPods - Refactored to standalone file
# Carthage - Refactored to standalone file
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
## Various settings
## Other
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
# End of https://www.gitignore.io/api/swift,xcode,carthage,cocoapods,objective-c
\ No newline at end of file
......@@ -2,14 +2,13 @@
# * http://www.objc.io/issue-6/travis-ci.html
# * https://github.com/supermarin/xcpretty#usage
osx_image: xcode7.3
language: objective-c
# cache: cocoapods
# podfile: Example/Podfile
# before_install:
# - gem install cocoapods # Since Travis is not always on latest version
# - pod install --project-directory=Example
install:
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
script:
- set -o pipefail && xcodebuild test -workspace Example/CedarMap.xcworkspace -scheme CedarMap-Example -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO | xcpretty -c
- pod lib lint --quick
- set -o pipefail && xcodebuild test -enableCodeCoverage YES -workspace Example/CedarMaps.xcworkspace -scheme CedarMaps-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty
- pod lib lint
#
# Be sure to run `pod lib lint CedarMaps.podspec' to ensure this is a
# valid spec and remove all comments before submitting the spec.
#
# Any lines starting with a # are optional, but encouraged
# valid spec before submitting.
#
# Any lines starting with a # are optional, but their use is encouraged
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'CedarMaps'
s.version = '1.0.2'
s.cocoapods_version = '>= 0.36'
s.license = 'MIT'
s.homepage = 'https://www.kikojas.com/about-cedarmaps'
s.authors = { 'Emad A.' => 'emad310@gmail.com', 'Saeed T' => 'saeed.taheri@gmail.com' }
s.version = '2.0.0'
s.summary = 'CedarMaps iOS SDK'
# This description is used to generate tags and improve search results.
# * Think: What does it do? Why did you write it? What is the focus?
# * Try to keep it short, snappy and to the point.
# * Write the description between the DESC delimiters below.
# * Finally, don't worry about the indent, CocoaPods strips it!
s.description = <<-DESC
The CedarMaps iOS SDK library is used to integrate CedarMaps tiles and using geocoding APIs in your iOS application.
For using this library, you need valid credentials. Please visit http://cedarmaps.com for more information.
DESC
s.homepage = 'https://www.cedarmaps.com'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.authors = { 'CedarMaps' => 'info@cedarmaps.com', 'Saeed Taheri' => 'saeed.taheri@gmail.com' }
s.source = { :git => 'http://gitlab.cedar.ir/cedar.studios/cedarmaps-sdk-ios-public.git', :tag => s.version.to_s }
s.source_files = 'Pod/Classes'
s.framework = 'UIKit'
s.platform = :ios, '8.0'
s.social_media_url = 'https://twitter.com/cedarmaps'
s.platform = :ios, '8.0'
s.requires_arc = true
s.public_header_files = 'Pod/Classes/**/*.h'
s.dependency 'Mapbox-iOS-SDK', '~> 3.3.4'
end
\ No newline at end of file
s.cocoapods_version = '>= 1.1'
s.source_files = 'CedarMaps/Classes/**/*'
s.public_header_files = 'CedarMaps/Headers/Public/*.h'
s.resource_bundles = {
'CedarMaps' => ['CedarMaps/Assets/*.xcassets']
}
s.public_header_files = 'CedarMaps/Classes/**/*.h'
s.framework = 'UIKit'
s.dependency 'Mapbox-iOS-SDK', '~> 3.6'
s.dependency 'JSONModel', '~> 1.7'
end
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
......@@ -2,16 +2,17 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "cedarmaps-attribution.png"
"filename" : "cedarmaps.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "cedarmaps-attribution@2x.png"
"filename" : "cedarmaps@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "cedarmaps@3x.png",
"scale" : "3x"
}
],
......
......@@ -2,17 +2,7 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "bus-stop.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "bus-stop@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "mapbox_cedarmaps_marker_icon_default.pdf"
}
],
"info" : {
......
#import <Foundation/Foundation.h>
static NSString * _Nonnull const kCedarMapsAccessTokenIsReadeyNotification = @"kCedarMapsAccessTokenIsReadeyNotification";
@interface CSAuthenticationManager : NSObject
@property (nonatomic, strong, readonly, nullable) NSString *clientID;
@property (nonatomic, strong, readonly, nullable) NSString *clientSecret;
@property (nonatomic, strong, nullable) NSString *baseURL;
@property (class, readonly, strong, nonnull) CSAuthenticationManager *sharedAuthenticationManager;
- (void)setCredentialsWithClientID:(nonnull NSString *)clientID clientSecret:(nonnull NSString *)clientSecret;
- (BOOL)isAccessTokenSaved;
- (void)accessToken:(nonnull void (^) ( NSString * _Nullable token, NSError * _Nullable error))completion;
- (void)refetchAccessToken;
@end
#import "CSAuthenticationManager.h"
#import "CSError.h"
static NSString * const kBaseURL = @"https://api.cedarmaps.com/v1/";
static NSString * const kCurrentAccessToken = @"CedarMapsSDKUserAccessToken_v1";
@interface CSAuthenticationManager ()
@property (nonatomic, strong) NSString *accessToken;
@property (nonatomic, assign) BOOL isFetchingAccessToken;
@end
@implementation CSAuthenticationManager
+ (instancetype)sharedAuthenticationManager {
static id _sharedManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
+ (NSURLSession *)sharedURLSession {
static id _sharedSession = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedSession = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:nil delegateQueue:[NSOperationQueue mainQueue]];
});
return _sharedSession;
}
- (instancetype)init
{
self = [super init];
if (self != nil) {
self.baseURL = kBaseURL;
self.isFetchingAccessToken = NO;
}
return self;
}
- (void)setBaseURL:(NSString *)baseURL {
if (baseURL) {
_baseURL = baseURL;
} else {
_baseURL = kBaseURL;
}
}
- (BOOL)isAccessTokenSaved {
return [[NSUserDefaults standardUserDefaults] objectForKey:kCurrentAccessToken];
}
- (void)accessToken:(nonnull void (^)(NSString * _Nullable, NSError * _Nullable))completion {
if (!_accessToken) {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
_accessToken = [defaults objectForKey:kCurrentAccessToken];
if (!_accessToken) {
[self requestAccessTokenFromServer:completion];
} else {
completion(_accessToken, nil);
}
} else {
completion(_accessToken, nil);
}
}
- (void)setCredentialsWithClientID:(nonnull NSString *)clientID clientSecret:(nonnull NSString *)clientSecret {
NSAssert(clientID != nil && clientID.length > 0, @"Given Client ID is empty.");
NSAssert(clientSecret != nil && clientSecret.length > 0, @"Given Client Secret is empty.");
_clientID = clientID;
_clientSecret = clientSecret;
}
- (void)requestAccessTokenFromServer:(nonnull void (^)(NSString *token, NSError *error))completion
{
NSAssert(self.clientID != nil, @"No Client ID was specified. Set your given credentials before trying to get an access token.");
NSAssert(self.clientSecret != nil, @"No Client Secret was specified. Set your given credentials before trying to get an access token.");
NSString *params = [NSString stringWithFormat:@"client_id=%@&client_secret=%@", self.clientID, self.clientSecret];
params = [params stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *tokenURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@token", self.baseURL]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:tokenURL];
[request setHTTPBody:[params dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPMethod:@"POST"];
[[[CSAuthenticationManager sharedURLSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable token, NSURLResponse * _Nullable response, NSError * _Nullable responseError) {
if (responseError != nil) {
completion(nil, responseError);
return;
} else if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = [(NSHTTPURLResponse *)response statusCode];
if (statusCode == 200) {
NSError *serializationError;
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:token options:0 error:&serializationError];
if (serializationError != nil) {
completion(nil, serializationError);
return;
}
_accessToken = result[@"access_token"];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:_accessToken forKey:kCurrentAccessToken];
if (_accessToken) {
[[NSNotificationCenter defaultCenter] postNotificationName:kCedarMapsAccessTokenIsReadeyNotification object:nil userInfo:@{kCedarMapsAccessTokenIsReadeyNotification: _accessToken}];
}
completion(_accessToken, nil);
return;
} else {
completion(nil, [NSError errorWithDomain:NSURLErrorDomain code:statusCode userInfo:nil]);
return;
}
} else {
completion(nil, [CSError errorWithDescription:@"Unknown error occurred in getting access token."]);
return;
}
}] resume];
}
- (void)refetchAccessToken {
if (self.isFetchingAccessToken) {
return;
}
self.isFetchingAccessToken = YES;
[self invalidateCredentials];
__weak CSAuthenticationManager *weakSelf = self;
[self requestAccessTokenFromServer:^(NSString *token, NSError *error) {
weakSelf.isFetchingAccessToken = NO;
}];
}
- (void)invalidateCredentials
{
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults removeObjectForKey:kCurrentAccessToken];
[defaults synchronize];
_accessToken = nil;
}
@end
//
// CSBoundingBox.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <Foundation/Foundation.h>
#import <JSONModel/JSONModel.h>
@import CoreLocation;
@protocol CSBoundingBox;
/**
* The class used for creating a rectangular region bu specifying south west and north east coordinates.
*/
@interface CSBoundingBox: JSONModel
/**
* The north east location of the bounding box
*/
@property (nonatomic, strong, nonnull) CLLocation *northEast;
/**
* The south west location of the bounding box
*/
@property (nonatomic, strong, nonnull) CLLocation *southWest;
/**
The designated initializer to use for creating a CSBoundingBox instance.
@param northEast The north east coordinate of the bounding box
@param southWest The south west coordinate of the bounding box
@return An instance of a CSBoundingBox object
*/
- (nonnull instancetype)initWithNorthEastCoordinate:(CLLocationCoordinate2D)northEast southWestCoordinate:(CLLocationCoordinate2D)southWest;
@end
//
// CSBoundingBox.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSBoundingBox.h"
@implementation CSBoundingBox
- (instancetype)initWithNorthEastCoordinate:(CLLocationCoordinate2D)northEast southWestCoordinate:(CLLocationCoordinate2D)southWest {
self = [super init];
if (self) {
self.northEast = [[CLLocation alloc] initWithLatitude:northEast.latitude longitude:northEast.longitude];
self.southWest = [[CLLocation alloc] initWithLatitude:southWest.latitude longitude:southWest.longitude];
}
return self;
}
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"northEast": @"ne",
@"southWest": @"sw"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
- (void)setNorthEastWithString:(NSString *)string {
_northEast = [self CLLocationFromNSString:string];
}
- (NSString *)JSONObjectForNorthEast {
return [self JSONObjectFromCLLocation:_northEast];
}
- (void)setSouthWestWithString:(NSString *)string {
_southWest = [self CLLocationFromNSString:string];
}
- (NSString *)JSONObjectForSouthWest {
return [self JSONObjectFromCLLocation:_southWest];
}
- (CLLocation *)CLLocationFromNSString:(NSString *)string {
NSArray *comps = [string componentsSeparatedByString:@","];
return [[CLLocation alloc] initWithLatitude:[comps[0] doubleValue] longitude:[comps[1] doubleValue]];
}
- (NSString *)JSONObjectFromCLLocation:(CLLocation *)location {
return [NSString stringWithFormat:@"%f,%f", location.coordinate.latitude, location.coordinate.longitude];
}
@end
//
// CSDirectionCompleteResponse.h
// CedarMaps
//
// Created by Saeed Taheri on 10/25/17.
//
#import <JSONModel/JSONModel.h>
#import "CSRoute.h"
@interface CSDirectionResponse : JSONModel
@property (nonatomic, strong, nonnull) NSString *status;
@property (nonatomic, strong, nullable) NSArray<CSRoute *> <CSRoute, Optional> *routes;
@end
//
// CSDirectionCompleteResponse.m
// CedarMaps
//
// Created by Saeed Taheri on 10/25/17.
//
#import "CSDirectionResponse.h"
@implementation CSDirectionResponse
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"routes": @"result.routes"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
@end
//
// CSError.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <Foundation/Foundation.h>
#define CEDARMAPS_RESPONSE_PARSING_ERROR @"Response Parsing Error"
#define CEDARMAPS_UNKNOWN_ERROR @"Unknown Error"
@interface CSError : NSError
+ (nonnull NSError *)errorWithDescription:(nonnull NSString *)errorDescription;
@end
//
// CSError.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSError.h"
@implementation CSError
+ (NSError *)errorWithDescription:(NSString *)errorDescription {
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: errorDescription};
return [NSError errorWithDomain:@"NSCedarMapsDomain" code:-69 userInfo:userInfo];
}
@end
//
// CSForwardGeocodePlacemark.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <JSONModel/JSONModel.h>
#import "CSRegion.h"
@protocol CSForwardGeocodePlacemark;
@class CSForwardGeocodeComponent;
/**
* Represents placemark data for a geographic location in a forward geocode request. Placemark data can be
* information such as the province, city, and street address.
*/
@interface CSForwardGeocodePlacemark: JSONModel
/**
The unique identifier for the result.
*/
@property (nonatomic, assign) NSInteger identifier;
/**
Name of the result.
*/
@property (nonatomic, strong, nonnull) NSString *name;
/**
English name of the result.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *nameEn;
/**
Type of the result. e.g. street, locality, place, etc.
*/
@property (nonatomic, strong, nonnull) NSString *type;
/**
A simple generated address from components field.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *address;
/**
Detailed address components for the result.
*/
@property (nonatomic, strong, nonnull) CSForwardGeocodeComponent *components;
/**
Geometric region of the result.
*/
@property (nonatomic, strong, nullable) CSRegion<Optional> *region;
@end
typedef void (^CSForwardGeocodeCompletionHandler)(NSArray<CSForwardGeocodePlacemark *> * __nullable placemarks, NSError * __nullable error);
typedef NS_OPTIONS(NSUInteger, CSPlacemarkType) {
CSPlacemarkTypeAll = 0,
CSPlacemarkTypeRoundabout = 1 << 0,
CSPlacemarkTypeStreet = 1 << 1,
CSPlacemarkTypeFreeway = 1 << 2,
CSPlacemarkTypeExpressway = 1 << 3,
CSPlacemarkTypeBoulevard = 1 << 4,
CSPlacemarkTypeLocality = 1 << 5,
};
NSString* _Nonnull stringValueForPlacemarkType(CSPlacemarkType type);
#pragma mark Components
/**
* Represents placemark components data for a geographic location in a forward geocode request.
*/
@interface CSForwardGeocodeComponent: JSONModel
@property (nonatomic, strong, nullable) NSString<Optional> *country;
@property (nonatomic, strong, nullable) NSString<Optional> *province;
@property (nonatomic, strong, nullable) NSString<Optional> *city;
@property (nonatomic, strong, nullable) NSArray<NSString *> <Optional> *districts;
@property (nonatomic, strong, nullable) NSArray<NSString *> <Optional> *localities;
@end
//
// CSForwardGeocodePlacemark.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSForwardGeocodePlacemark.h"
@implementation CSForwardGeocodePlacemark
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"identifier": @"id",
@"nameEn": @"name_en",
@"region": @"location"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
@end
@implementation CSForwardGeocodeComponent
@end
NSString* stringValueForPlacemarkType(CSPlacemarkType type) {
NSString *result = @"";
if (type & CSPlacemarkTypeAll) {
return result;
}
if (type & CSPlacemarkTypeStreet) {
result = [result stringByAppendingString:@"street,"];
}
if (type & CSPlacemarkTypeFreeway) {
result = [result stringByAppendingString:@"freeway,"];
}
if (type & CSPlacemarkTypeLocality) {
result = [result stringByAppendingString:@"locality,"];
}
if (type & CSPlacemarkTypeBoulevard) {
result = [result stringByAppendingString:@"boulevard,"];
}
if (type & CSPlacemarkTypeExpressway) {
result = [result stringByAppendingString:@"expressway,"];
}
if (type & CSPlacemarkTypeRoundabout) {
result = [result stringByAppendingString:@"roundabout,"];
}
if (result.length > 1) {
result = [result substringToIndex:result.length - 1];
}
return result;
}
//
// CSForwardGeocodeResponse.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <JSONModel/JSONModel.h>
#import "CSForwardGeocodePlacemark.h"
@interface CSForwardGeocodeResponse : JSONModel
@property (nonatomic, strong, nonnull) NSString *status;
@property (nonatomic, strong, nullable) NSArray<CSForwardGeocodePlacemark *> <CSForwardGeocodePlacemark, Optional> *results;
@end
//
// CSForwardGeocodeResponse.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSForwardGeocodeResponse.h"
@implementation CSForwardGeocodeResponse
@end
@import CoreLocation;
#import "CSForwardGeocodePlacemark.h"
#import "CSReverseGeocodePlacemark.h"
#import "CSRoute.h"
#import "CSRoutePair.h"
#import "CSBoundingBox.h"
#import "CSMapSnapshot.h"
#import "CSMapSnapshotOptions.h"
/**
* This class is the single point entry for using CedarMaps API on iOS.
* The setup and using methods process are done through this class.
*
* You should ALWAYS use the provided sharedMapKit class property to initialize an instance
* of this class.
*/
@interface CSMapKit: NSObject
/**
* The shared CSMapKit session which can be used to setup, configure,
* and use CedarMaps API methods.
*
*/
@property (class, readonly, strong, nonnull) CSMapKit *sharedMapKit;
/**
* Use this method to setup CedarMaps API and should be called before any other methods in this framework.
*
* ClientID and ClientSecret are given to you when you asked for CedarMaps API access.
@param clientID The Client ID you were given
@param clientSecret The Client Secret you were given
*/
- (void)setCredentialsWithClientID:(nonnull NSString *)clientID
clientSecret:(nonnull NSString *)clientSecret;
/**
* Use this method to set the baseURL for using CedarMaps API.
* If you are given a different baseURL for using CedarMaps API, set it here.
* This method should be called during setup before using any of the CedarMaps methods.
@param urlString If you pass nil, the SDK uses the default baseURL.
*/
- (void)setAPIBaseURL:(nullable NSString*)urlString;
/**
This method should be called before using CedarMaps map tiles using CSMapView.
@param completionHandler This handler is called when the process of preparing map tiles
* is completed. The block will be called on the main_queue.
@see CSMapView.h
*/
- (void)prepareMapTiles:(nonnull void (^) (BOOL isReady, NSError * _Nullable error))completionHandler;
/**
This method will get you the textual address components related to a particular coordiante.
@param location The location for which the address components would be generated.
@param completionHandler This handler is called when the process of fetching reverse geocode
* result is completed. The block will be called on the main_queue.
*/
- (void)reverseGeocodeLocation:(nonnull CLLocation *)location
completionHandler:(nonnull CSReverseGeocodeCompletionHandler)completionHandler;
/**
This method will search for an address using the provided string query.
@param addressString The address query to search.
@param completionHandler This handler is called when the process of fetching forward geocode
* results is completed. The block will be called on the main_queue.
*/
- (void)geocodeAddressString:(nonnull NSString *)addressString
completionHandler:(nonnull CSForwardGeocodeCompletionHandler)completionHandler;
/**
This method will search for an address using the provided string query.
@param addressString The address query to search.
@param type The placemark types to include in search results. This can be a combination of values.
@param limit The maximum number of results to return.
@param completionHandler This handler is called when the process of fetching forward geocode
* results is completed. The block will be called on the main_queue.
*/
- (void)geocodeAddressString:(nonnull NSString *)addressString
withType:(CSPlacemarkType)type
limit:(NSInteger)limit
completionHandler:(nonnull CSForwardGeocodeCompletionHandler)completionHandler;
/**
This method will search for an address using the provided string query.
@param addressString The address query to search.
@param region The circular region consisting of a center coordinate and a radius to limit the search results.
@param completionHandler This handler is called when the process of fetching forward geocode
* results is completed. The block will be called on the main_queue.
*/
- (void)geocodeAddressString:(nonnull NSString *)addressString
inRegion:(nonnull CLCircularRegion *)region
completionHandler:(nonnull CSForwardGeocodeCompletionHandler)completionHandler;
/**
This method will search for an address using the provided string query.
@param addressString The address query to search.
@param region The circular region consisting of a center coordinate and a radius to limit the search results.
@param type The placemark types to include in search results. This can be a combination of values.
@param limit The maximum number of results to return.
@param completionHandler This handler is called when the process of fetching forward geocode
* results is completed. The block will be called on the main_queue.
*/
- (void)geocodeAddressString:(nonnull NSString *)addressString
inRegion:(nonnull CLCircularRegion *)region
withType:(CSPlacemarkType)type
limit:(NSInteger)limit
completionHandler:(nonnull CSForwardGeocodeCompletionHandler)completionHandler;
/**
This method will search for an address using the provided string query.
@param addressString The address query to search.
@param boundingBox The rectangular region consisting of a north east and south west coordinates to limit the search results.
@param completionHandler This handler is called when the process of fetching forward geocode
* results is completed. The block will be called on the main_queue.
*/
- (void)geocodeAddressString:(nonnull NSString *)addressString
inBoundingBox:(nonnull CSBoundingBox *)boundingBox
completionHandler:(nonnull CSForwardGeocodeCompletionHandler)completionHandler;
/**
This method will search for an address using the provided string query.
@param addressString The address query to search.
@param boundingBox The rectangular region consisting of a north east and south west coordinates to limit the search results.
@param type The placemark types to include in search results. This can be a combination of values.
@param limit The maximum number of results to return.
@param completionHandler This handler is called when the process of fetching forward geocode
* results is completed. The block will be called on the main_queue.
*/
- (void)geocodeAddressString:(nonnull NSString *)addressString
inBoundingBox:(nonnull CSBoundingBox *)boundingBox
withType:(CSPlacemarkType)type
limit:(NSInteger)limit
completionHandler:(nonnull CSForwardGeocodeCompletionHandler)completionHandler;
/**
* This method calculates directions using car profile between a source and a destination.
*
* Up to 100 pairs of source and destionation points can be provided to calculate a multiple step routing.
@param routePairs An array of CSRoutePair consisting of source and destination points.
@param completionHandler This handler is called when the process of fetching direction
* results is completed. The block will be called on the main_queue.
@see CSRoutePair.h
*/
- (void)calculateDirections:(nonnull NSArray<CSRoutePair *> *)routePairs withCompletionHandler:(nonnull CSDirectionCompletionHandler)completionHandler;
/**
* This method calculates distance using car profile between a source and a destination.
*
* Up to 100 pairs of source and destionation points can be provided to calculate a multiple step routing.
@param routePairs An array of CSRoutePair consisting of source and destination points.
@param completionHandler This handler is called when the process of fetching distance
* results is completed. The block will be called on the main_queue.
@see CSRoutePair.h
*/
- (void)calculateDistance:(nonnull NSArray<CSRoutePair *> *)routePairs withCompletionHandler:(nonnull CSDirectionCompletionHandler)completionHandler;
/**
This methods creates a snapshot image of a map with provided options.
@param options The options for creating a map image. Size, Zoom level, Custom markers, etc.
@param completionHandler This handler is called when the process of fetching the map image is completed. The block will be called on the main_queue.
*/
- (void)createMapSnapshotWithOptions:(nonnull CSMapSnapshotOptions *)options withCompletionHandler:(nonnull CSMapSnapshotCompletionHandler)completionHandler;
@end
This diff is collapsed.
//
// CSMapSnapshot.h
// CedarMaps
//
// Created by Saeed Taheri on 10/29/17.
//
#import <UIKit/UIKit.h>
@import CoreLocation;
/**
The result which is returned in the completion handler for a CSMapSnapshot request
*/
@interface CSMapSnapshot : NSObject
/**
The image result of a map snapshot.
*/
@property (nonatomic, strong, nullable) UIImage *image;
@end
typedef void (^CSMapSnapshotCompletionHandler)(CSMapSnapshot * _Nullable snapshot, NSError * _Nullable error);
//
// CSMapSnapshot.m
// CedarMaps
//
// Created by Saeed Taheri on 10/29/17.
//
#import "CSMapSnapshot.h"
@implementation CSMapSnapshot
@end
//
// CSMapSnapshotMarker.h
// CedarMaps
//
// Created by Saeed Taheri on 10/29/17.
//
#import <Foundation/Foundation.h>
@import CoreLocation;
/**
Custom markers for drawing on a map snapshot request
*/
@interface CSMapSnapshotMarker : NSObject
/**
Coordinate of the marker.
*/
@property (nonatomic, assign, readonly) CLLocationCoordinate2D coordinate;
/**
The remote URL of the marker. If nil, the default marker is used.
*/
@property (nonatomic, strong, nullable, readonly) NSURL *url;
/**
Initializer for creating a marker for using in a map snapshot request.
@param coordinate Coordinate of the marker.
@param url The remote url of desired marker image.
@return An instance of CSMapSnapshotMarker.
*/
- (nonnull instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate markerURL:(nonnull NSURL *)url;
/**
Initializer for creating a marker with default appearance for using in a map snapshot request.
@param coordinate Coordinate of the marker.
@return An instance of CSMapSnapshotMarker.
*/
- (nonnull instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate;
@end
//
// CSMapSnapshotMarker.m
// CedarMaps
//
// Created by Saeed Taheri on 10/29/17.
//
#import "CSMapSnapshotMarker.h"
@interface CSMapSnapshotMarker()
@property (nonatomic, assign) CLLocationCoordinate2D coordinate;
@property (nonatomic, strong, nullable) NSURL *url;
@end
@implementation CSMapSnapshotMarker
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate {
self = [super init];
if (self) {
self.coordinate = coordinate;
self.url = nil;
}
return self;
}
- (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate markerURL:(NSURL *)url {
self = [super init];
if (self) {
self.coordinate = coordinate;
self.url = url;
}
return self;
}
@end
//
// CSMapSnapshotOptions.h
// CedarMaps
//
// Created by Saeed Taheri on 10/29/17.
//
#import <Foundation/Foundation.h>
#import "CSMapSnapshotMarker.h"
@import CoreLocation;
/**
The options for customizing a map snapshot request.
*/
@interface CSMapSnapshotOptions : NSObject
/**
Desired size of the map image in points.
*/
@property (nonatomic, assign) CGSize size;
/**
Desired zoom level of the map.
*/
@property (nonatomic, assign) NSInteger zoomLevel;
/**
Center location of the map.
*/
@property (nonatomic, strong, nullable) CLLocation *center;
/**
Custom markers to draw on map snapshot.
*/
@property (nonatomic, strong, nullable) NSArray<CSMapSnapshotMarker *> *markers;
@end
//
// CSMapSnapshotOptions.m
// CedarMaps
//
// Created by Saeed Taheri on 10/29/17.
//
#import "CSMapSnapshotOptions.h"
@implementation CSMapSnapshotOptions
- (instancetype)init {
self = [super init];
if (self) {
self.size = CGSizeMake(800, 600);
self.zoomLevel = 14;
}
return self;
}
@end
//
// CSMapView.h
// CedarMaps
//
// Created by Saeed Taheri on 10/22/17.
//
#import <UIKit/UIKit.h>
@import Mapbox;
/**
* A subclass of MGLMapView tailored for using with CedarMaps tiles.
*
* If you use MGLMapView directly, CedarMaps tiles can't be loaded.
*/
@interface CSMapView : MGLMapView
@end
//
// CSMapView.m
// CedarMaps
//
// Created by Saeed Taheri on 10/22/17.
//
#import "CSMapView.h"
#import "CSAuthenticationManager.h"
@interface MGLMapView()
- (MGLAnnotationImage *)defaultAnnotationImage;
@end
@implementation CSMapView
- (instancetype)init
{
self = [super init];
if (self) {
[self initialSetup];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self initialSetup];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initialSetup];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame styleURL:(NSURL *)styleURL {
self = [super initWithFrame:frame styleURL:styleURL];
if (self) {
[self initialSetup];
}
return self;
}
- (void)initialSetup {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
[self.logoView setImage:[UIImage imageNamed:@"cedarmaps" inBundle:bundle compatibleWithTraitCollection:nil]];
self.attributionButton.alpha = 0;
self.layer.backgroundColor = [UIColor colorWithRed: 249.0/255.0 green: 245.0/255.0 blue: 237.0/255.0 alpha: 1.0].CGColor;
[[NSNotificationCenter defaultCenter] addObserverForName:kCedarMapsAccessTokenIsReadeyNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
NSString *token = [note.userInfo objectForKey:kCedarMapsAccessTokenIsReadeyNotification];
[MGLAccountManager setAccessToken:token];
[self setupStyleURL];
}];
if ([[CSAuthenticationManager sharedAuthenticationManager] isAccessTokenSaved]) {
[self setupStyleURL];
}
[self setNeedsLayout];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setupStyleURL {
__weak CSMapView *weakSelf = self;
[[CSAuthenticationManager sharedAuthenticationManager] accessToken:^(NSString * _Nullable token, NSError * _Nullable error) {
if (token) {
NSString *urlStr = [NSString stringWithFormat:@"%@tiles/light.json?access_token=%@", [[CSAuthenticationManager sharedAuthenticationManager] baseURL], token];
NSString *encodedURLStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
[weakSelf setStyleURL:[NSURL URLWithString:encodedURLStr]];
}
}];
}
- (MGLAnnotationImage *)defaultAnnotationImage
{
MGLAnnotationImage *annotationImage = [super defaultAnnotationImage];
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
UIImage *image = [UIImage imageNamed:@"cedarmaps_default" inBundle:bundle compatibleWithTraitCollection:nil];
image = [image imageWithAlignmentRectInsets:
UIEdgeInsetsMake(0, 0, image.size.height / 2, 0)];
[annotationImage setImage:image];
return annotationImage;
}
- (void)layoutSubviews {
[super layoutSubviews];
CGFloat rightInset = self.contentInset.right + 8;
CGFloat bottomInset = self.contentInset.bottom + 8;
if (@available(iOS 11.0, *)) {
rightInset += self.safeAreaInsets.right;
bottomInset += self.safeAreaInsets.bottom;
}
self.logoView.frame = CGRectMake(CGRectGetWidth(self.bounds) - CGRectGetWidth(self.logoView.bounds) - rightInset,
CGRectGetHeight(self.bounds) - CGRectGetHeight(self.logoView.bounds) - bottomInset,
CGRectGetWidth(self.logoView.bounds),
CGRectGetHeight(self.logoView.bounds));
}
@end
//
// CSRegion.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <JSONModel/JSONModel.h>
#import "CSBoundingBox.h"
@import CoreLocation;
/**
*
* Geometric region of a Forward Geocode result.
*/
@interface CSRegion: JSONModel
/**
*
* The center location of a Forward Geocode result.
*/
@property (nonatomic, strong, nonnull) CLLocation *center;
/**
The bounding box of a Forward Geocode result.
*/
@property (nonatomic, strong, nonnull) CSBoundingBox *boundingBox;
@end
//
// CSRegion.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSRegion.h"
@implementation CSRegion
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"boundingBox": @"bb",
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
- (void)setBoundingBoxWithNSDictionary:(NSDictionary *)dic {
NSArray *neComps = [[dic objectForKey:@"ne"] componentsSeparatedByString:@","];
NSArray *swComps = [[dic objectForKey:@"sw"] componentsSeparatedByString:@","];
CLLocationCoordinate2D ne = CLLocationCoordinate2DMake([neComps[0] doubleValue], [neComps[1] doubleValue]);
CLLocationCoordinate2D sw = CLLocationCoordinate2DMake([swComps[0] doubleValue], [swComps[1] doubleValue]);
_boundingBox = [[CSBoundingBox alloc] initWithNorthEastCoordinate:ne southWestCoordinate:sw];
}
- (NSDictionary *)JSONObjectForBoundingBox {
if (_boundingBox == nil) {
return nil;
}
return @{@"ne": [NSString stringWithFormat:@"%f,%f", _boundingBox.northEast.coordinate.latitude, _boundingBox.northEast.coordinate.longitude],
@"sw": [NSString stringWithFormat:@"%f,%f", _boundingBox.southWest.coordinate.latitude, _boundingBox.southWest.coordinate.longitude]
};
}
- (void)setCenterWithNSString:(NSString *)string {
NSArray *comps = [string componentsSeparatedByString:@","];
_center = [[CLLocation alloc] initWithLatitude:[comps[0] doubleValue] longitude:[comps[1] doubleValue]];
}
- (NSString *)JSONObjectForCenter {
if (_center == nil) {
return nil;
}
return [NSString stringWithFormat:@"%f,%f", _center.coordinate.latitude, _center.coordinate.longitude];
}
@end
//
// CSReverseGeocodePlacemark.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <JSONModel/JSONModel.h>
@class CSTrafficZone;
@class CSReverseGeocodeComponent;
@protocol CSReverseGeocodeComponent;
/**
* Represents placemark data for a geographic location in a reverse geocode request. Placemark data can be
* information such as the province, city, and street address.
*/
@interface CSReverseGeocodePlacemark: JSONModel
/**
Address for the result.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *address;
/**
Locality name for the result.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *locality;
/**
District name for the result.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *district;
/**
Place name for the result. If it exists.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *place;
/**
City name for the result.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *city;
/**
Province name for the result.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *province;
/**
Traffic zone information for the result.
*/
@property (nonatomic, strong, nullable) CSTrafficZone<Optional> *trafficZone;
/**
Detailed address components for the result.
*/
@property (nonatomic, strong, nullable) NSArray<CSReverseGeocodeComponent *> <CSReverseGeocodeComponent, Optional> *components;
@end
typedef void (^CSReverseGeocodeCompletionHandler)(CSReverseGeocodePlacemark* __nullable placemark, NSError * __nullable error);
#pragma mark - Traffic Zone
/**
* Traffic zone information for a Reverse Geocode response
*/
@interface CSTrafficZone: JSONModel
/**
Name of the Traffic Zone.
*/
@property (nonatomic, strong, nullable) NSString<Optional> *name;
/**
Shows if the location is in Central Traffic Zone.
*/
@property (nonatomic, assign, getter=isInCentral) BOOL inCentral;
/**
Shows if the location is in Even/Odd Traffic Zone.
*/
@property (nonatomic, assign, getter=isInEvenOdd) BOOL inEvenOdd;
@end
#pragma mark - Components
/**
* Detailed components of a Reverse Geocode response
*/
@interface CSReverseGeocodeComponent: JSONModel
/**
Short name of the component for a result
*/
@property (nonatomic, strong, nonnull) NSString<Optional> *shortName;
/**
Long name of the component for a result
*/
@property (nonatomic, strong, nonnull) NSString<Optional> *longName;
/**
Type of the component for a result
*/
@property (nonatomic, strong, nonnull) NSString *type;
@end
//
// CSReverseGeocodePlacemark.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSReverseGeocodePlacemark.h"
@implementation CSReverseGeocodePlacemark
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"trafficZone": @"traffic_zone"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
@end
#pragma mark Traffic Zone
@implementation CSTrafficZone
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"inEvenOdd": @"in_evenodd",
@"inCentral": @"in_central"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
@end
#pragma mark Component
@implementation CSReverseGeocodeComponent
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{ @"longName": @"long_name",
@"shortName": @"short_name"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
@end
//
// CSReverseGeocodeResponse.h
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import <JSONModel/JSONModel.h>
#import "CSReverseGeocodePlacemark.h"
@interface CSReverseGeocodeResponse : JSONModel
@property (nonatomic, strong, nonnull) NSString *status;
@property (nonatomic, strong, nullable) CSReverseGeocodePlacemark<Optional> *result;
@end
//
// CSReverseGeocodeResponse.m
// CedarMaps
//
// Created by Saeed Taheri on 10/24/17.
//
#import "CSReverseGeocodeResponse.h"
@implementation CSReverseGeocodeResponse
@end
//
// CSDirectionResponse.h
// CedarMaps
//
// Created by Saeed Taheri on 10/25/17.
//
#import <JSONModel/JSONModel.h>
#import "CSBoundingBox.h"
@protocol CSRoute;
/**
* A CSRoute object shows direction and distance information.
*/
@interface CSRoute: JSONModel
/**
Total distance in a route in meters using car profile.
*/
@property (nonatomic, assign) CLLocationDistance distance;
/**
ETA for a route in seconds using car profile.
*/
@property (nonatomic, assign) NSTimeInterval time;
/**
Bounding box for a route.
*/
@property (nonatomic, strong, nonnull) CSBoundingBox *boundingBox;
/**
The location points which make a route.
*/
@property (nonatomic, strong, nullable) NSArray<CLLocation *> <Optional> *points;
@end
typedef void (^CSDirectionCompletionHandler)(NSArray<CSRoute *> * __nullable routes, NSError * __nullable error);
//
// CSDirectionResponse.m
// CedarMaps
//
// Created by Saeed Taheri on 10/25/17.
//
#import "CSRoute.h"
@implementation CSRoute
- (void)setBoundingBoxWithNSArray:(NSArray *)array {
if (array && array.count == 4) {
CLLocationCoordinate2D ne = CLLocationCoordinate2DMake([array[3] doubleValue], [array[2] doubleValue]);
CLLocationCoordinate2D sw = CLLocationCoordinate2DMake([array[1] doubleValue], [array[0] doubleValue]);
_boundingBox = [[CSBoundingBox alloc] initWithNorthEastCoordinate:ne southWestCoordinate:sw];
}
}
- (NSArray *)JSONObjectForBoundingBox {
if (_boundingBox == nil) {
return nil;
}
return @[
[NSNumber numberWithDouble:_boundingBox.southWest.coordinate.longitude],
[NSNumber numberWithDouble:_boundingBox.southWest.coordinate.latitude],
[NSNumber numberWithDouble:_boundingBox.northEast.coordinate.longitude],
[NSNumber numberWithDouble:_boundingBox.northEast.coordinate.latitude]
];
}
- (void)setPointsWithNSArray:(NSArray *)array {
NSMutableArray *points = [NSMutableArray arrayWithCapacity:array.count];
for (NSArray *item in array) {
CLLocation *location = [[CLLocation alloc] initWithLatitude:[item[1] doubleValue] longitude:[item[0] doubleValue]];
[points addObject:location];
}
_points = [points copy];
}
- (NSArray *)JSONObjectForPoints {
if (_points == nil) {
return nil;
}
NSMutableArray *coordinates = [NSMutableArray arrayWithCapacity:_points.count];
for (CLLocation *item in _points) {
NSMutableArray *itemArray = [NSMutableArray arrayWithCapacity:2];
[itemArray addObject:[NSNumber numberWithDouble:item.coordinate.longitude]];
[itemArray addObject:[NSNumber numberWithDouble:item.coordinate.latitude]];
[coordinates addObject:itemArray.copy];
}
return [coordinates copy];
}
+ (JSONKeyMapper *)keyMapper {
NSDictionary *map = @{
@"points": @"geometry.coordinates",
@"boundingBox": @"bbox"
};
return [[JSONKeyMapper alloc] initWithModelToJSONDictionary:map];
}
@end
//
// CSRoutePair.h
// CedarMaps
//
// Created by Saeed Taheri on 10/25/17.
//
#import <Foundation/Foundation.h>
@import CoreLocation;
/**
A wrapper class consisting of a source and a destination for using in a Directions or Distance request.
*/
@interface CSRoutePair : NSObject
/**
Source location of the route.
*/
@property (nonatomic, strong, nonnull) CLLocation *source;
/**
Destination location for the route.
*/
@property (nonatomic, strong, nonnull) CLLocation *destination;
/**
Designated initializer for creating a CSRoutePair.
@param source Source location of the route.
@param destination Destination location of the route.
@return An instance of CSRoutePair object.
*/
- (nonnull instancetype)initWithSource:(nonnull CLLocation *)source destination:(nonnull CLLocation *)destination;
@end
//
// CSRoutePair.m
// CedarMaps
//
// Created by Saeed Taheri on 10/25/17.
//
#import "CSRoutePair.h"
@implementation CSRoutePair
- (instancetype)initWithSource:(CLLocation *)source destination:(CLLocation *)destination {
self = [super init];
if (self) {
self.source = source;
self.destination = destination;
}
return self;
}
@end
@import Mapbox;
#import "CSMapKit.h"
#import "CSMapView.h"
This diff is collapsed.
......@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:CedarMap.xcodeproj">
location = "self:CedarMaps.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0800"
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
......@@ -15,8 +15,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "CedarMaps.app"
BlueprintName = "CedarMaps"
BuildableName = "CedarMaps_Example.app"
BlueprintName = "CedarMaps_Example"
ReferencedContainer = "container:CedarMaps.xcodeproj">
</BuildableReference>
</BuildActionEntry>
......@@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
......@@ -33,8 +34,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F5AD195388D20070C39A"
BuildableName = "Tests.xctest"
BlueprintName = "Tests"
BuildableName = "CedarMaps_Tests.xctest"
BlueprintName = "CedarMaps_Tests"
ReferencedContainer = "container:CedarMaps.xcodeproj">
</BuildableReference>
</TestableReference>
......@@ -43,8 +44,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "CedarMaps.app"
BlueprintName = "CedarMaps"
BuildableName = "CedarMaps_Example.app"
BlueprintName = "CedarMaps_Example"
ReferencedContainer = "container:CedarMaps.xcodeproj">
</BuildableReference>
</MacroExpansion>
......@@ -55,22 +56,34 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
allowLocationSimulation = "YES"
showNonLocalizedStrings = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "CedarMaps.app"
BlueprintName = "CedarMaps"
BuildableName = "CedarMaps_Example.app"
BlueprintName = "CedarMaps_Example"
ReferencedContainer = "container:CedarMaps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "-AppleLanguages &quot;(fa)&quot;"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
argument = "-AppleLocale &quot;fa_IR&quot;"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
......@@ -85,8 +98,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "6003F589195388D20070C39A"
BuildableName = "CedarMaps.app"
BlueprintName = "CedarMaps"
BuildableName = "CedarMaps_Example.app"
BlueprintName = "CedarMaps_Example"
ReferencedContainer = "container:CedarMaps.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
......
......@@ -2,6 +2,9 @@
<Workspace
version = "1.0">
<FileRef
location = "self:Example_Swift.xcodeproj">
location = "group:CedarMaps.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
This diff is collapsed.
/*
Localizable.strings
CedarMaps
Created by Saeed Taheri on 11/1/17.
Copyright © 2017 Saeed Taheri. All rights reserved.
*/
"Search" = "Search";
"Input Empty" = "Input Empty";
"Please fill all fields with appropriate values" = "Please fill all fields with appropriate values";
"OK" = "OK";
"Response Error" = "Response Error";
"Directions Error" = "Directions Error";
/* Class = "UITabBarItem"; title = "Reverse Geocode"; ObjectID = "2cy-Oc-jdY"; */
"2cy-Oc-jdY.title" = "Reverse Geocode";
/* Class = "UIButton"; normalTitle = "Create Snapshot"; ObjectID = "3eM-lp-ybO"; */
"3eM-lp-ybO.normalTitle" = "Create Snapshot";
/* Class = "UILabel"; text = "Label"; ObjectID = "5dd-fE-gjh"; */
"5dd-fE-gjh.text" = "Label";
/* Class = "UILabel"; text = "Tap on the map to choose Departure and Destination points"; ObjectID = "6Rq-z4-inE"; */
"6Rq-z4-inE.text" = "Tap on the map to choose Departure and Destination points";
/* Class = "UITextField"; text = "35.7454"; ObjectID = "7Kd-VU-rTF"; */
"7Kd-VU-rTF.text" = "35.7454";
/* Class = "UITabBarItem"; title = "Directions"; ObjectID = "Eni-Nt-rLL"; */
"Eni-Nt-rLL.title" = "Directions";
/* Class = "UITabBarItem"; title = "Map"; ObjectID = "FaA-0j-SaR"; */
"FaA-0j-SaR.title" = "Map";
/* Class = "UILabel"; text = "Label"; ObjectID = "Knc-SY-RVC"; */
"Knc-SY-RVC.text" = "Label";
/* Class = "UILabel"; text = "Zoom Level"; ObjectID = "Lra-Fx-K0F"; */
"Lra-Fx-K0F.text" = "Zoom Level";
/* Class = "UITabBarItem"; title = "Search"; ObjectID = "M4J-v4-I5V"; */
"M4J-v4-I5V.title" = "Search";
/* Class = "UITextField"; text = "51.3572"; ObjectID = "PuK-NC-TRb"; */
"PuK-NC-TRb.text" = "51.3572";
/* Class = "UILabel"; text = "Label"; ObjectID = "X4G-5I-PLA"; */
"X4G-5I-PLA.text" = "Label";
/* Class = "UILabel"; text = "Longitude for Center Point"; ObjectID = "cKo-k8-k6G"; */
"cKo-k8-k6G.text" = "Longitude for Center Point";
/* Class = "UILabel"; text = "Distance: "; ObjectID = "dgd-97-9ef"; */
"dgd-97-9ef.text" = "Distance: ";
/* Class = "UILabel"; text = "Label"; ObjectID = "hLz-Kf-n3t"; */
"hLz-Kf-n3t.text" = "Label";
/* Class = "UIButton"; normalTitle = "Reset"; ObjectID = "iuq-mN-QRR"; */
"iuq-mN-QRR.normalTitle" = "Reset";
/* Class = "UITabBarItem"; title = "Snapshot"; ObjectID = "nz9-6B-3Hs"; */
"nz9-6B-3Hs.title" = "Snapshot";
/* Class = "UITextField"; text = "13"; ObjectID = "oFs-ns-oJV"; */
"oFs-ns-oJV.text" = "13";
/* Class = "UILabel"; text = "Latitude for Center Point"; ObjectID = "ozJ-AE-hPZ"; */
"ozJ-AE-hPZ.text" = "Latitude for Center Point";
/* Class = "UILabel"; text = "Tap on the button to create a map snapshot for entered values."; ObjectID = "uvq-Cl-QFn"; */
"uvq-Cl-QFn.text" = "Tap on the button to create a map snapshot for entered values.";
/* Class = "UILabel"; text = "6.53 Km"; ObjectID = "yGq-pA-gbd"; */
"yGq-pA-gbd.text" = "6.53 Km";
/* Class = "UITabBarItem"; title = "Reverse Geocode"; ObjectID = "2cy-Oc-jdY"; */
"2cy-Oc-jdY.title" = "نقطه به آدرس";
/* Class = "UIButton"; normalTitle = "Create Snapshot"; ObjectID = "3eM-lp-ybO"; */
"3eM-lp-ybO.normalTitle" = "ساخت نقشه استاتیک";
/* Class = "UILabel"; text = "Label"; ObjectID = "5dd-fE-gjh"; */
"5dd-fE-gjh.text" = "برچسب";
/* Class = "UILabel"; text = "Tap on the map to choose Departure and Destination points"; ObjectID = "6Rq-z4-inE"; */
"6Rq-z4-inE.text" = "برای انتخاب مبدأ و مقصد، نقشه را لمس کنید";
/* Class = "UITextField"; text = "35.7454"; ObjectID = "7Kd-VU-rTF"; */
"7Kd-VU-rTF.text" = "35.7454";
/* Class = "UITabBarItem"; title = "Directions"; ObjectID = "Eni-Nt-rLL"; */
"Eni-Nt-rLL.title" = "مسیریابی";
/* Class = "UITabBarItem"; title = "Map"; ObjectID = "FaA-0j-SaR"; */
"FaA-0j-SaR.title" = "نقشه";
/* Class = "UILabel"; text = "Label"; ObjectID = "Knc-SY-RVC"; */
"Knc-SY-RVC.text" = "برچسب";
/* Class = "UILabel"; text = "Zoom Level"; ObjectID = "Lra-Fx-K0F"; */
"Lra-Fx-K0F.text" = "زوم";
/* Class = "UITabBarItem"; title = "Search"; ObjectID = "M4J-v4-I5V"; */
"M4J-v4-I5V.title" = "جستجو";
/* Class = "UITextField"; text = "51.3572"; ObjectID = "PuK-NC-TRb"; */
"PuK-NC-TRb.text" = "51.3572";
/* Class = "UILabel"; text = "Label"; ObjectID = "X4G-5I-PLA"; */
"X4G-5I-PLA.text" = "برچسب";
/* Class = "UILabel"; text = "Longitude for Center Point"; ObjectID = "cKo-k8-k6G"; */
"cKo-k8-k6G.text" = "طول جغرفیایی نقطه مرکزی";
/* Class = "UILabel"; text = "Distance: "; ObjectID = "dgd-97-9ef"; */
"dgd-97-9ef.text" = "مسافت: ";
/* Class = "UILabel"; text = "Label"; ObjectID = "hLz-Kf-n3t"; */
"hLz-Kf-n3t.text" = "برچسب";
/* Class = "UIButton"; normalTitle = "Reset"; ObjectID = "iuq-mN-QRR"; */
"iuq-mN-QRR.normalTitle" = "انتخاب دوباره";
/* Class = "UITabBarItem"; title = "Snapshot"; ObjectID = "nz9-6B-3Hs"; */
"nz9-6B-3Hs.title" = "نقشه استاتیک";
/* Class = "UITextField"; text = "13"; ObjectID = "oFs-ns-oJV"; */
"oFs-ns-oJV.text" = "13";
/* Class = "UILabel"; text = "Latitude for Center Point"; ObjectID = "ozJ-AE-hPZ"; */
"ozJ-AE-hPZ.text" = "عرض جغرافیایی نقطه مرکزی";
/* Class = "UILabel"; text = "Tap on the button to create a map snapshot for entered values."; ObjectID = "uvq-Cl-QFn"; */
"uvq-Cl-QFn.text" = "برای ایجاد نقشه با مقادیر وارد شده، دکمه را لمس کنید.";
/* Class = "UILabel"; text = "6.53 Km"; ObjectID = "yGq-pA-gbd"; */
"yGq-pA-gbd.text" = "6.53 Km";
//
// CSAppDelegate.h
// CedarMap
// CedarMaps
//
// Created by CocoaPods on 01/19/2015.
// Copyright (c) 2014 Emad A.. All rights reserved.
// Created by Saeed Taheri on 10/21/2017.
// Copyright (c) 2017 Saeed Taheri. All rights reserved.
//
#import <UIKit/UIKit.h>
@import UIKit;
@interface CSAppDelegate : UIResponder <UIApplicationDelegate>
......
//
// CSAppDelegate.m
// CedarMap
// CedarMaps
//
// Created by CocoaPods on 01/19/2015.
// Copyright (c) 2014 Emad A.. All rights reserved.
// Created by Saeed Taheri on 10/21/2017.
// Copyright (c) 2017 Saeed Taheri. All rights reserved.
//
#import "CSAppDelegate.h"
@import CedarMaps;
@implementation CSAppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
[[CSMapKit sharedMapKit] setCredentialsWithClientID:@"YOUR_CLIENT_ID" clientSecret:@"YOUR_CLIENT_SECRET"];
[[CSMapKit sharedMapKit] prepareMapTiles:^(BOOL isReady, NSError * _Nullable error) {
}];
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
......@@ -24,7 +31,7 @@
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
......
//
// CSDirectionsViewController.h
// CedarMaps_Example
//
// Created by Saeed Taheri on 10/30/17.
// Copyright © 2017 Saeed Taheri. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface CSDirectionsViewController : UIViewController
@end
//
// CSDirectionsViewController.m
// CedarMaps_Example
//
// Created by Saeed Taheri on 10/30/17.
// Copyright © 2017 Saeed Taheri. All rights reserved.
//
#import "CSDirectionsViewController.h"
@import CedarMaps;
@interface CSDirectionsViewController () <MGLMapViewDelegate>
@property (weak, nonatomic) IBOutlet CSMapView *mapView;
@property (weak, nonatomic) IBOutlet UIStackView *distanceStackView;
@property (weak, nonatomic) IBOutlet UILabel *distanceLabel;
@property (weak, nonatomic) IBOutlet UIButton *resetButton;
@property (weak, nonatomic) IBOutlet UIView *hintView;
@property (strong, nonatomic) NSMutableArray<MGLPointAnnotation *> *markers;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
@property (strong, nonatomic) NSMeasurementFormatter *distanceFormatter;
@end
@implementation CSDirectionsViewController
- (void)setResetButton:(UIButton *)resetButton {
resetButton.layer.cornerRadius = 10.0;
resetButton.layer.masksToBounds = YES;
[resetButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[resetButton setBackgroundColor:self.tabBarController.tabBar.tintColor];
resetButton.hidden = YES;
_resetButton = resetButton;
}
- (void)setDistanceStackView:(UIStackView *)distanceStackView {
distanceStackView.hidden = YES;
_distanceStackView = distanceStackView;
}
- (void)setSpinner:(UIActivityIndicatorView *)spinner {
spinner.hidden = YES;
_spinner = spinner;
}
- (NSMutableArray<MGLPointAnnotation *> *)markers {
if (!_markers) {
_markers = [NSMutableArray arrayWithCapacity:2];
}
return _markers;
}
- (NSMeasurementFormatter *)distanceFormatter {
if (!_distanceFormatter) {
_distanceFormatter = [[NSMeasurementFormatter alloc] init];
_distanceFormatter.unitStyle = NSFormattingUnitStyleShort;
_distanceFormatter.unitOptions = NSMeasurementFormatterUnitOptionsNaturalScale;
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
numberFormatter.alwaysShowsDecimalSeparator = NO;
numberFormatter.numberStyle = NSNumberFormatterDecimalStyle;
numberFormatter.maximumFractionDigits = 2;
_distanceFormatter.numberFormatter = numberFormatter;
}
return _distanceFormatter;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView.delegate = self;
[self setupSingleTapGestureOnMapView];
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator {
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:NULL completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if (self.mapView.annotations) {
[self.mapView showAnnotations:self.mapView.annotations animated:YES];
}
}];
}
- (void)setupSingleTapGestureOnMapView {
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
tgr.numberOfTapsRequired = 1;
tgr.numberOfTouchesRequired = 1;
//This loop goes through the gesture recognizers of default mapview to avoid conflicts with the added one.
for (UIGestureRecognizer *recognizer in self.mapView.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[tgr requireGestureRecognizerToFail:recognizer];
}
}
[self.mapView addGestureRecognizer:tgr];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)sender {
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:[sender locationInView:sender.view] toCoordinateFromView:sender.view];
if (self.markers.count == 0) {
[self addMarkerAtCoordinate:coordinate];
} else if (self.markers.count == 1) {
[self addMarkerAtCoordinate:coordinate];
CLLocation *location0 = [[CLLocation alloc] initWithLatitude:self.markers[0].coordinate.latitude longitude:self.markers[0].coordinate.longitude];
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:self.markers[1].coordinate.latitude longitude:self.markers[1].coordinate.longitude];
[self computeDirectionsFromSource:location0 toDestination:location1];
}
}
- (IBAction)resetMap:(UIButton *)sender {
[self resetToInitialState];
}
- (void)resetToInitialState {
self.resetButton.hidden = YES;
self.distanceStackView.hidden = YES;
self.hintView.hidden = NO;
self.spinner.hidden = YES;
[self.markers removeAllObjects];
if (self.mapView.annotations) {
[self.mapView removeAnnotations:self.mapView.annotations];
}
}
- (void)computeDirectionsFromSource:(CLLocation *)source toDestination:(CLLocation *)destination {
self.hintView.hidden = YES;
[self.spinner startAnimating];
self.spinner.hidden = NO;
__weak CSDirectionsViewController *weakSelf = self;
[[CSMapKit sharedMapKit] calculateDirections:@[[[CSRoutePair alloc] initWithSource:source destination:destination]] withCompletionHandler:^(NSArray<CSRoute *> * _Nullable routes, NSError * _Nullable error) {
[weakSelf.spinner stopAnimating];
[weakSelf.spinner setHidden:YES];
CSRoute *route = routes.firstObject;
if (route) {
NSMeasurement *distance = [[NSMeasurement alloc] initWithDoubleValue:route.distance unit:[NSUnitLength meters]];
weakSelf.distanceLabel.text = [weakSelf.distanceFormatter stringFromMeasurement:distance];
weakSelf.distanceStackView.hidden = NO;
weakSelf.resetButton.hidden = NO;
[UIView animateWithDuration:0.3 animations:^{
[weakSelf.view layoutIfNeeded];
} completion:^(BOOL finished) {
if (route.points) {
[weakSelf drawLocations:route.points];
}
}];
} else if (error) {
[weakSelf resetToInitialState];
UIAlertController *alert = [UIAlertController alertControllerWithTitle: NSLocalizedString(@"Directions Error", "") message:error.localizedDescription preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", "") style:UIAlertActionStyleCancel handler:NULL]];
[weakSelf presentViewController:alert animated:YES completion:NULL];
}
}];
}
- (void)drawLocations:(NSArray<CLLocation *> *)locations {
CLLocationCoordinate2D coordinates[locations.count];
for (int i = 0; i < locations.count; i++) {
coordinates[i] = locations[i].coordinate;
}
MGLPolyline *polyline = [[MGLPolyline alloc] init];
[polyline setCoordinates:coordinates count:locations.count];
[self.mapView addAnnotation:polyline];
[self.mapView showAnnotations:self.mapView.annotations animated:YES];
}
- (void)addMarkerAtCoordinate:(CLLocationCoordinate2D)coordinate {
MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
annotation.coordinate = coordinate;
[self.markers addObject:annotation];
[self.mapView addAnnotation:annotation];
}
- (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id<MGLAnnotation>)annotation {
if (annotation == self.markers.firstObject) {
UIImage *image = [UIImage imageNamed:@"marker_icon_start"];
image = [image imageWithAlignmentRectInsets:
UIEdgeInsetsMake(0, 0, image.size.height / 2, 0)];
return [MGLAnnotationImage annotationImageWithImage:image reuseIdentifier:@"marker_start_identifier"];
} else if (annotation == self.markers.lastObject) {
UIImage *image = [UIImage imageNamed:@"marker_icon_end"];
image = [image imageWithAlignmentRectInsets:
UIEdgeInsetsMake(0, 0, image.size.height / 2, 0)];
return [MGLAnnotationImage annotationImageWithImage:image reuseIdentifier:@"marker_end_identifier"];
}
return nil;
}
- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation {
return 0.9;
}
- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation {
return 6.0;
}
@end
//
// CSForwardGeocodeViewController.swift
// CedarMaps_Example
//
// Created by Saeed Taheri on 10/31/17.
// Copyright © 2017 Saeed Taheri. All rights reserved.
//
import UIKit
import CedarMaps
class CSForwardGeocodeViewController: UIViewController {
private var filteredResults = [CSForwardGeocodePlacemark]() {
didSet {
selectedItem = nil
tableView.reloadData()
UIView.animate(withDuration: 0.3) {
self.tableView.alpha = self.filteredResults.count > 0 ? 1.0 : 0.0
}
}
}
private var selectedItem: CSForwardGeocodePlacemark? {
didSet {
if let existingAnnotations = mapView.annotations {
mapView.removeAnnotations(existingAnnotations)
}
if let selectedItem = selectedItem, let coordinate = selectedItem.region?.center.coordinate {
searchController.searchBar.resignFirstResponder()
let annotation = MGLPointAnnotation()
annotation.coordinate = coordinate
annotation.title = selectedItem.name
annotation.subtitle = selectedItem.address
mapView.addAnnotation(annotation)
UIView.animate(withDuration: 0.3, animations: {
self.tableView.alpha = 0
}) { _ in
self.mapView.setCenter(coordinate, zoomLevel: 15, animated: true)
}
}
}
}
@IBOutlet private weak var mapView: CSMapView! {
didSet {
mapView.delegate = self
}
}
@IBOutlet private weak var tableView: UITableView! {
didSet {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView.alpha = 0
tableView.keyboardDismissMode = .onDrag
}
}
private var pendingSearchWorkItem: DispatchWorkItem?
private var isSearchBarEmpty: Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
private lazy var searchController: UISearchController = {
let result = UISearchController(searchResultsController: nil)
result.searchResultsUpdater = self
result.obscuresBackgroundDuringPresentation = false
definesPresentationContext = true
return result
}()
override func viewDidLoad() {
super.viewDidLoad()
title = NSLocalizedString("Search", comment: "")
if #available(iOS 11, *) {
navigationItem.searchController = searchController
navigationItem.hidesSearchBarWhenScrolling = false
} else {
navigationItem.titleView = searchController.searchBar
searchController.searchBar.sizeToFit()
}
}
private func filterContentForSearchText(_ searchText: String) {
pendingSearchWorkItem?.cancel()
let searchWorkItem = DispatchWorkItem {
CSMapKit.shared.geocodeAddressString(searchText) { [weak self] (results, error) in
if let results = results, results.count > 0 {
self?.filteredResults = results
} else {
self?.filteredResults = [CSForwardGeocodePlacemark]()
}
}
}
pendingSearchWorkItem = searchWorkItem
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300), execute: searchWorkItem)
}
}
extension CSForwardGeocodeViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return filteredResults.count > 0 ? 1 : 0
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredResults.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CSSearchResultCell", for: indexPath) as! CSSearchResultCell
cell.placemark = filteredResults[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
selectedItem = filteredResults[indexPath.row]
}
}
extension CSForwardGeocodeViewController: UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController) {
filterContentForSearchText(searchController.searchBar.text!)
}
}
extension CSForwardGeocodeViewController: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
}
//
// CSMapSnapshotViewController.swift
// CedarMaps_Example
//
// Created by Saeed Taheri on 10/30/17.
// Copyright © 2017 Saeed Taheri. All rights reserved.
//
import UIKit
import CedarMaps
class CSMapSnapshotViewController: KeyboardEntryViewController {
@IBOutlet private weak var imageView: UIImageView!
@IBOutlet private weak var hintLabel: UILabel!
@IBOutlet private weak var spinner: UIActivityIndicatorView!
@IBOutlet private weak var scrollView: UIScrollView! {
didSet {
mainScrollView = scrollView
}
}
@IBOutlet private weak var latitudeTextField: UITextField! {
didSet {
latitudeTextField.delegate = self
}
}
@IBOutlet private weak var longitudeTextField: UITextField! {
didSet {
longitudeTextField.delegate = self
}
}
@IBOutlet private weak var zoomLevelTextField: UITextField! {
didSet {
zoomLevelTextField.delegate = self
}
}
@IBOutlet private weak var submitButton: UIButton! {
didSet {
submitButton.setTitleColor(UIColor.white, for: .normal)
submitButton.backgroundColor = self.tabBarController?.tabBar.tintColor
submitButton.layer.cornerRadius = 10.0
submitButton.layer.masksToBounds = true
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
coordinator.animate(alongsideTransition: nil) { (context) in
if let _ = self.view.window, let imageView = self.imageView, let _ = imageView.image {
self.createSnapshot()
}
}
}
@IBAction private func submit(_ sender: UIButton?) {
createSnapshot()
}
private func createSnapshot() {
view.endEditing(true)
guard let latitudeStr = latitudeTextField?.text, !latitudeStr.isEmpty, let latitude = Double(latitudeStr),
let longitudeStr = longitudeTextField?.text, !longitudeStr.isEmpty, let longitude = Double(longitudeStr),
let zoomLevelStr = zoomLevelTextField?.text, !zoomLevelStr.isEmpty, let zoomLevel = Int(zoomLevelStr)
else {
let alert = UIAlertController(title: NSLocalizedString("Input Empty", comment: ""), message: NSLocalizedString("Please fill all fields with appropriate values", comment: ""), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .cancel, handler: nil))
present(alert, animated: true, completion: nil)
return
}
let options = CSMapSnapshotOptions()
options.center = CLLocation(latitude: latitude, longitude: longitude)
options.zoomLevel = zoomLevel
options.size = imageView.bounds.size
// options.markers = [CSMapSnapshotMarker(coordinate: options.center!.coordinate)]
spinner.isHidden = false
spinner.startAnimating()
hintLabel.isHidden = true
submitButton.isEnabled = false
CSMapKit.shared.createMapSnapshot(with: options) { [weak self] (snapshot, error) in
self?.spinner.stopAnimating()
self?.spinner.isHidden = true
self?.submitButton.isEnabled = true
if let error = error {
let alert = UIAlertController(title: NSLocalizedString("Response Error", comment: ""), message: error.localizedDescription, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: ""), style: .cancel, handler: nil))
self?.present(alert, animated: true, completion: nil)
return
}
if let image = snapshot?.image {
self?.imageView.image = image
}
if self?.imageView.image == nil {
self?.hintLabel.isHidden = false
}
}
}
}
extension CSMapSnapshotViewController {
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
switch textField {
case latitudeTextField:
longitudeTextField.becomeFirstResponder()
case longitudeTextField:
zoomLevelTextField.becomeFirstResponder()
case zoomLevelTextField:
textField.resignFirstResponder()
createSnapshot()
default:
return true
}
return false
}
}
//
// CSViewController.h
// CedarMaps
//
// Created by Saeed Taheri on 10/21/2017.
// Copyright (c) 2017 Saeed Taheri. All rights reserved.
//
@import UIKit;
@interface CSMapViewController : UIViewController
@end
//
// CSViewController.m
// CedarMaps
//
// Created by Saeed Taheri on 10/21/2017.
// Copyright (c) 2017 Saeed Taheri. All rights reserved.
//
#import "CSMapViewController.h"
@import CedarMaps;
@interface CSMapViewController ()<MGLMapViewDelegate, CLLocationManagerDelegate>
@property (weak, nonatomic) IBOutlet CSMapView *mapView;
@property (weak, nonatomic) IBOutlet UIButton *currentLocationButton;
@property (strong, nonatomic) CLLocationManager *locationManager;
@end
@implementation CSMapViewController
- (CLLocationManager *)locationManager {
if (!_locationManager) {
_locationManager = [[CLLocationManager alloc] init];
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
_locationManager.delegate = self;
}
return _locationManager;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView.delegate = self;
if ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse) {
[self.mapView setShowsUserLocation:YES];
}
[self setupCurrentLocationButtonVisuals];
[self setupSingleTapGestureOnMapView];
[self addMarkerAtCoordinate:self.mapView.centerCoordinate];
}
- (IBAction)showCurrentLocation:(id)sender {
switch ([CLLocationManager authorizationStatus]) {
case kCLAuthorizationStatusAuthorizedWhenInUse:
if (self.mapView.userLocation.location && CLLocationCoordinate2DIsValid(self.mapView.userLocation.location.coordinate)) {
[self.mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate animated:YES];
}
break;
case kCLAuthorizationStatusNotDetermined:
[self.locationManager requestWhenInUseAuthorization];
break;
default:
break;
}
}
- (void)setupSingleTapGestureOnMapView {
UITapGestureRecognizer *tgr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSingleTap:)];
tgr.numberOfTapsRequired = 1;
tgr.numberOfTouchesRequired = 1;
//This loop goes through the gesture recognizers of default mapview to avoid conflicts with the added one.
for (UIGestureRecognizer *recognizer in self.mapView.gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[tgr requireGestureRecognizerToFail:recognizer];
}
}
[self.mapView addGestureRecognizer:tgr];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)sender {
if (self.mapView.annotations) {
[self.mapView removeAnnotations:self.mapView.annotations];
}
CLLocationCoordinate2D coordinate = [self.mapView convertPoint:[sender locationInView:sender.view] toCoordinateFromView:sender.view];
[self addMarkerAtCoordinate:coordinate];
}
- (void)addMarkerAtCoordinate:(CLLocationCoordinate2D)coordinate {
MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
annotation.coordinate = coordinate;
[self.mapView addAnnotation:annotation];
}
- (void)setupCurrentLocationButtonVisuals {
CALayer *layer = self.currentLocationButton.layer;
layer.cornerRadius = 12.0;
layer.masksToBounds = NO;
layer.shadowOffset = CGSizeMake(0, 0);
layer.shadowColor = [[UIColor blackColor] CGColor];
layer.shadowRadius = 12.0f;
layer.shadowOpacity = 0.1f;
layer.shadowPath = [[UIBezierPath bezierPathWithRoundedRect:layer.bounds cornerRadius:layer.cornerRadius] CGPath];
layer.backgroundColor = [UIColor whiteColor].CGColor;
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
switch (status) {
case kCLAuthorizationStatusAuthorizedWhenInUse:
[self.mapView setShowsUserLocation:YES];
break;
default:
break;
}
}
@end
//
// CSReverseGeocodeViewController.swift
// CedarMaps_Example
//
// Created by Saeed Taheri on 10/29/17.
// Copyright © 2017 Saeed Taheri. All rights reserved.
//
import UIKit
import CedarMaps
class CSReverseGeocodeViewController: UIViewController {
@IBOutlet private weak var mapView: CSMapView! {
didSet {
mapView.delegate = self
}
}
@IBOutlet private weak var labelBackgroundView: UIView! {
didSet {
let layer = labelBackgroundView.layer
layer.cornerRadius = 12.0
layer.masksToBounds = false
layer.shadowOffset = CGSize.zero
layer.shadowColor = UIColor.black.cgColor
layer.shadowRadius = 12.0
layer.shadowOpacity = 0.1
layer.rasterizationScale = UIScreen.main.scale
layer.shouldRasterize = true
layer.backgroundColor = UIColor.white.cgColor;
}
}
@IBOutlet private weak var label: UILabel! {
didSet {
label.isHidden = true
label.adjustsFontForContentSizeCategory = true
}
}
@IBOutlet private weak var spinner: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
reverseGeocode(coordinate: mapView.centerCoordinate)
}
private func reverseGeocode(coordinate: CLLocationCoordinate2D) {
spinner.isHidden = false
spinner.startAnimating()
label.isHidden = true
let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)
CSMapKit.shared.reverseGeocodeLocation(location) { [weak self] (placemark, error) in
defer {
self?.spinner.stopAnimating()
self?.spinner.isHidden = true
self?.label.isHidden = false
}
if let placemark = placemark {
self?.label.text = placemark.fullAddress
} else if let error = error {
self?.label.text = error.localizedDescription
}
}
}
}
extension CSReverseGeocodeViewController: MGLMapViewDelegate {
func mapView(_ mapView: MGLMapView, regionDidChangeAnimated animated: Bool) {
reverseGeocode(coordinate: mapView.centerCoordinate)
}
}
extension CSReverseGeocodePlacemark {
var fullAddress: String {
var result = ""
if let province = province, !province.isEmpty {
result += "استان " + province;
}
if let city = city, !city.isEmpty {
if result.isEmpty {
result = city
} else {
result += "، " + city
}
}
if let locality = locality, !locality.isEmpty {
if result.isEmpty {
result = locality
} else {
result += "، " + locality
}
}
if let address = address, !address.isEmpty {
if result.isEmpty {
result = address
} else {
result += "، " + address
}
}
if let place = place, !place.isEmpty {
if result.isEmpty {
result = place
} else {
result += "، " + place
}
}
return result
}
}
//
// SearchResultCell.swift
// Cedar Maps
//
// Created by Saeed Taheri on 7/14/17.
// Copyright © 2017 Cedar Studios. All rights reserved.
//
import UIKit
import CedarMaps
class CSSearchResultCell: UITableViewCell {
var placemark: CSForwardGeocodePlacemark! {
didSet {
typeLabel.text = placemark.persianType
nameLabel.text = placemark.name
addressLabel.text = placemark.fullAddress
addressLabel.isHidden = addressLabel.text!.isEmpty
}
}
@IBOutlet private weak var typeLabel: UILabel! {
didSet {
typeLabel.font = UIFont.preferredFont(forTextStyle: .footnote)
typeLabel.textColor = UIColor(red: 204.0/255.0, green: 204.0/255.0, blue: 204.0/255.0, alpha: 1.0)
}
}
@IBOutlet private weak var nameLabel: UILabel! {
didSet {
nameLabel.font = UIFont.preferredFont(forTextStyle: .headline)
}
}
@IBOutlet private weak var addressLabel: UILabel! {
didSet {
addressLabel.font = UIFont.preferredFont(forTextStyle: .body)
addressLabel.textColor = UIColor(red: 204.0/255.0, green: 204.0/255.0, blue: 204.0/255.0, alpha: 1.0)
}
}
}
extension CSForwardGeocodePlacemark {
var fullAddress: String {
var result = ""
if let city = components.city, !city.isEmpty {
result += city
}
if let address = address, !address.isEmpty {
if result.isEmpty {
result = address
} else {
result = result + "، " + address
}
} else if let localities = components.localities, localities.count > 0 {
var locality = ""
for i in 0..<min(2, localities.count) {
let l = localities[i]
if (locality.isEmpty) {
locality = l;
} else {
locality = locality + "، " + l;
}
}
if !locality.isEmpty {
if result.isEmpty {
result = locality
} else {
result = result + "، " + locality
}
}
}
return result.replacingOccurrences(of: ",", with: "، ")
}
var persianType: String {
let streetType = [
"freeway": "آزادراه",
"expressway": "بزرگراه",
"road": "جاده",
"boulevard": "بلوار",
"roundabout": "میدان",
"street": "خیابان",
"locality": "محله",
"poi": "مکان",
"region": "منطقه"
]
return streetType[type] ?? "نامشخص"
}
}
......@@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<string>CedarMaps Dev</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
......@@ -30,11 +30,11 @@
<true/>
</dict>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Maps Needs Location</string>
<string>CedarMaps-Dev wants to show your location on the map.</string>
<key>UILaunchStoryboardName</key>
<string>Launch Screen</string>
<key>UIMainStoryboardFile</key>
<string>Main_iPhone</string>
<key>UIMainStoryboardFile~ipad</key>
<string>Main_iPad</string>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
......
......@@ -11,6 +11,6 @@
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@import UIKit;
@import Foundation;
#endif
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
{
"images" : [
{
"idiom" : "iphone",
"size" : "20x20",
"idiom" : "iphone",
"filename" : "NotificationIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "20x20",
"idiom" : "iphone",
"filename" : "NotificationIcon@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-Small@3x.png",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@2x.png",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"size" : "57x57",
"idiom" : "iphone",
"filename" : "App Icon 120x120.png",
"filename" : "Icon.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "Icon@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-60@3x.png",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"idiom" : "ipad",
"filename" : "NotificationIcon~ipad.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "20x20",
"idiom" : "ipad",
"filename" : "NotificationIcon~ipad@2x.png",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small.png",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-Small@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "Icon-Small-50.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"size" : "40x40",
"filename" : "Icon-Small-50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"size" : "76x76",
"filename" : "Icon-72.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"size" : "76x76",
"filename" : "Icon-72@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "ios-marketing.png",
"scale" : "1x"
}
],
"info" : {
......
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
......@@ -2,21 +2,15 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "bus-stop.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "bus-stop@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "arrow.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "original",
"preserves-vector-representation" : true
}
}
\ No newline at end of file
......@@ -2,21 +2,15 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "1.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "1@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "direction.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
\ No newline at end of file
......@@ -2,21 +2,15 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "3.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "3@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "forward_geocode.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
\ No newline at end of file
......@@ -2,21 +2,15 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "star.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "star@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "map_marker.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template",
"preserves-vector-representation" : true
}
}
\ No newline at end of file
......@@ -2,21 +2,14 @@
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "1.png"
},
{
"idiom" : "universal",
"scale" : "2x",
"filename" : "1@2x.png"
},
{
"idiom" : "universal",
"scale" : "3x"
"filename" : "mapbox_cedarmaps_marker_icon_default.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "original"
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment