├── LICENSE.txt ├── README.md ├── SRAttractionsMap.podspec ├── SRAttractionsMap.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── Ruslan.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── Ruslan.xcuserdatad │ └── xcschemes │ ├── SRAttractionsMap.xcscheme │ └── xcschememanagement.plist ├── SRAttractionsMap ├── Annotation View │ ├── SRAnnotationView.swift │ ├── SRAttraction.swift │ └── annotation_icon.png ├── Callout View │ ├── SRCalloutView.swift │ ├── SRCalloutView.xib │ ├── SRTriangleView.swift │ └── close_icon.png ├── Info.plist └── SRAttractionsMapViewController.swift ├── SRAttractionsMapExample ├── SRAttractionsMapExample.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── Ruslan.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── Ruslan.xcuserdatad │ │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ │ └── xcschemes │ │ ├── SRAttractionsMapExample.xcscheme │ │ └── xcschememanagement.plist └── SRAttractionsMapExample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── 1.imageset │ │ ├── 1.png │ │ └── Contents.json │ ├── 2.imageset │ │ ├── 2.png │ │ └── Contents.json │ ├── 3.imageset │ │ ├── 3.png │ │ └── Contents.json │ ├── 4.imageset │ │ ├── 4.png │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── CoffeeshopsList.plist │ ├── Info.plist │ └── ViewController.swift └── gif.gif /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SRAttractionsMap ![Pod status](https://cocoapod-badges.herokuapp.com/v/SRAttractionsMap/badge.png) 2 | --- 3 | This is the map with attractions on which you can click and see the full description. 4 |

5 | Demo gif 6 |

7 | 8 | Installation 9 | --- 10 | The most preferable way to use this library is cocoapods. Add the following line to your `Podfile`: 11 | ```sh 12 | pod 'SRAttractionsMap' 13 | ``` 14 | and run `pod install` in your terminal. 15 | 16 | Alternatively, you can manually add the files in the `SRAttractionsMap` directory to your project. 17 | 18 | Usage 19 | -- 20 | First of all, you should add NSLocationWhenInUseUsageDescription to you info.plist with a brief explanation. 21 | 22 | Then you need to have an array of attractions(SRAttraction objects) to show: 23 | ```swift 24 | let attraction = SRAttraction(latitude: 52.362315, longitude: 4.857548) 25 | ``` 26 | You can specify different parameters for your attraction: 27 | ```swift 28 | attraction.name = title 29 | attraction.subname = subtitle 30 | attraction.image = image 31 | ``` 32 | 33 | After you attractions are ready you should create a custom instance of SRAttractionsMapViewController(subclass of UIViewController): 34 | ```swift 35 | let mapVC = SRAttractionsMapViewController(attractions: attractions, displayMode: .allAttractions) 36 | ``` 37 | or: 38 | ```swift 39 | let mapVC = SRAttractionsMapViewController(customPinImage: pinIcon, attractions: attractions, displayMode: .allAttractions) 40 | ``` 41 | The second convenience initializer allows you to use your custom icon for pins on the map 42 | 43 | Display mode decides how the map gonna be zoomed in initially after appearing of the view controller. You have few options: 44 | ```swift 45 | /// All attractions will be shown on the map 46 | case allAttractions 47 | 48 | /// The first location as the initial point and the radius is the distance to the second attraction times zoomRadiusMultiplier 49 | case firstTwoAttractions 50 | 51 | /// The user's location as the initial point and the radius is the distance to the first attraction times zoomRadiusMultiplier 52 | case userAndFirstAttraction 53 | 54 | /// The user's location as the initial point and the radius is the distance to the closest attraction times zoomRadiusMultiplier 55 | case userAndTheClosestLocation 56 | ``` 57 | 58 | After that you View Controller is ready and you can present it: 59 | ```swift 60 | self.present(mapVC, animated: true, completion: nil) 61 | ``` 62 | or: 63 | ```swift 64 | let nVC = UINavigationController(rootViewController: mapVC) 65 | self.present(nVC, animated: true, completion: nil) 66 | ``` 67 | 68 | If you want to add CTA on your attraction's view, please specify the title for the button: 69 | ```swift 70 | mapVC.calloutDetailButtonTitle = "View directions" 71 | ``` 72 | And the action for every attraction it its object(SRAttraction): 73 | ```swift 74 | attraction.detailAction = { _ in 75 | let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: attraction.coordinate, addressDictionary:nil)) 76 | mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeWalking]) 77 | } 78 | ``` 79 | 80 | You can also customize few more properties: 81 | ```swift 82 | /// The multiplier radius of initial zooming for display modes 83 | /// firstTwoAttractions/userAndFirstAttraction/userAndTheClosestLocation 84 | public var zoomRadiusMultiplier: Double = 2 85 | /// If this theshold in the mode .userAndTheClosestLocation exceeds 86 | /// - the map will be switched to the .allAttractions mode 87 | public var closestDistanceThreshold: CLLocationDistance = 10000 88 | 89 | /// Animation duration. Set to nil if you don't want to have the animation 90 | public var calloutFadeInAnimationDuration: TimeInterval? = 0.25 91 | /// The size of the callout view which shows when a pin is selected 92 | public var calloutViewSize: CGSize! 93 | 94 | /// Font for the title of an attraction 95 | public var calloutTitleFont: UIFont? 96 | /// Text color for the title of an attraction 97 | public var calloutTitleColor: UIColor? 98 | 99 | /// Font for the title of an attraction 100 | public var calloutSubtitleFont: UIFont? 101 | /// Text color for the title of an attraction 102 | public var calloutSubtitleColor: UIColor? 103 | 104 | /// Font for the text on the CTA button on the callout view 105 | public var calloutDetailButtonFont: UIFont? 106 | /// Text color for the CTA button on the callout view 107 | public var calloutDetailButtonTextColor: UIColor? 108 | 109 | /// Moves a selected pin to the center if true 110 | public var shouldScrollToPin = true 111 | /// Custom marker for attractions on the map 112 | public var customPinImage: UIImage? 113 | ``` 114 | 115 | Check out my other libraries 116 | -- 117 | 118 | [SmileToUnlock](https://github.com/rsrbk/SmileToUnlock) - uses ARKit Face Tracking in order to catch a user's smile.
119 | [SRCountdownTimer](https://github.com/rsrbk/SRCountdownTimer) - a simple circle countdown with a configurable timer. 120 | 121 | License 122 | -- 123 | MIT License 124 | 125 | Copyright (c) 2017 Ruslan Serebriakov 126 | 127 | Permission is hereby granted, free of charge, to any person obtaining a copy 128 | of this software and associated documentation files (the "Software"), to deal 129 | in the Software without restriction, including without limitation the rights 130 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 131 | copies of the Software, and to permit persons to whom the Software is 132 | furnished to do so, subject to the following conditions: 133 | 134 | The above copyright notice and this permission notice shall be included in all 135 | copies or substantial portions of the Software. 136 | 137 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 138 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 139 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 140 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 141 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 142 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 143 | SOFTWARE. 144 | -------------------------------------------------------------------------------- /SRAttractionsMap.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'SRAttractionsMap' 3 | s.version = '1.0.3' 4 | s.summary = 'This is the map which contains attractions added on it.' 5 | 6 | s.description = <<-DESC 7 | The map contains attractions placed on it with the abillity to click and see the full description. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/rsrbk/SRAttractionsMap.git' 11 | s.license = { :type => 'MIT', :file => 'LICENSE.txt' } 12 | s.author = { 'Ruslan Serebriakov' => 'rsrbk1@gmail.com' } 13 | s.source = { :git => 'https://github.com/rsrbk/SRAttractionsMap.git', :tag => s.version.to_s } 14 | 15 | s.ios.deployment_target = '11.0' 16 | s.source_files = 'SRAttractionsMap/**/*.swift' 17 | s.resources = 'SRAttractionsMap/Callout\ View/close_icon.png', 'SRAttractionsMap/Callout\ View/SRCalloutView.xib', 'SRAttractionsMap/Annotation\ View/annotation_icon.png' 18 | end 19 | -------------------------------------------------------------------------------- /SRAttractionsMap.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 441B20A11FC5A1B4007A728E /* close_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 441B20A01FC5A1B4007A728E /* close_icon.png */; }; 11 | 448B71B31FC0DDF6005C69C2 /* SRAttractionsMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448B71961FC0DDF6005C69C2 /* SRAttractionsMapViewController.swift */; }; 12 | 448B71DB1FC0DECA005C69C2 /* SRCalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448B71D81FC0DECA005C69C2 /* SRCalloutView.swift */; }; 13 | 448B71DC1FC0DECA005C69C2 /* SRCalloutView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 448B71D91FC0DECA005C69C2 /* SRCalloutView.xib */; }; 14 | 448B71DD1FC0DECA005C69C2 /* SRTriangleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448B71DA1FC0DECA005C69C2 /* SRTriangleView.swift */; }; 15 | 448B71E21FC0DEF9005C69C2 /* annotation_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 448B71DF1FC0DEF9005C69C2 /* annotation_icon.png */; }; 16 | 448B71E31FC0DEF9005C69C2 /* SRAttraction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448B71E01FC0DEF9005C69C2 /* SRAttraction.swift */; }; 17 | 448B71E41FC0DEF9005C69C2 /* SRAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 448B71E11FC0DEF9005C69C2 /* SRAnnotationView.swift */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 441B20A01FC5A1B4007A728E /* close_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = close_icon.png; path = "Callout View/close_icon.png"; sourceTree = ""; }; 22 | 448B716D1FC0DD9F005C69C2 /* SRAttractionsMap.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SRAttractionsMap.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 448B71711FC0DD9F005C69C2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 24 | 448B71961FC0DDF6005C69C2 /* SRAttractionsMapViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SRAttractionsMapViewController.swift; sourceTree = ""; }; 25 | 448B71D81FC0DECA005C69C2 /* SRCalloutView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SRCalloutView.swift; path = "Callout View/SRCalloutView.swift"; sourceTree = ""; }; 26 | 448B71D91FC0DECA005C69C2 /* SRCalloutView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = SRCalloutView.xib; path = "Callout View/SRCalloutView.xib"; sourceTree = ""; }; 27 | 448B71DA1FC0DECA005C69C2 /* SRTriangleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SRTriangleView.swift; path = "Callout View/SRTriangleView.swift"; sourceTree = ""; }; 28 | 448B71DF1FC0DEF9005C69C2 /* annotation_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = annotation_icon.png; path = "Annotation View/annotation_icon.png"; sourceTree = ""; }; 29 | 448B71E01FC0DEF9005C69C2 /* SRAttraction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SRAttraction.swift; path = "Annotation View/SRAttraction.swift"; sourceTree = ""; }; 30 | 448B71E11FC0DEF9005C69C2 /* SRAnnotationView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SRAnnotationView.swift; path = "Annotation View/SRAnnotationView.swift"; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 448B71691FC0DD9F005C69C2 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 448B71631FC0DD9F005C69C2 = { 45 | isa = PBXGroup; 46 | children = ( 47 | 448B716F1FC0DD9F005C69C2 /* SRAttractionsMap */, 48 | 448B716E1FC0DD9F005C69C2 /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 448B716E1FC0DD9F005C69C2 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 448B716D1FC0DD9F005C69C2 /* SRAttractionsMap.framework */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 448B716F1FC0DD9F005C69C2 /* SRAttractionsMap */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 448B71DE1FC0DECE005C69C2 /* Callout View */, 64 | 448B71E51FC0DEFC005C69C2 /* Annotation View */, 65 | 448B71961FC0DDF6005C69C2 /* SRAttractionsMapViewController.swift */, 66 | 448B71711FC0DD9F005C69C2 /* Info.plist */, 67 | ); 68 | path = SRAttractionsMap; 69 | sourceTree = ""; 70 | }; 71 | 448B71DE1FC0DECE005C69C2 /* Callout View */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 441B20A01FC5A1B4007A728E /* close_icon.png */, 75 | 448B71D81FC0DECA005C69C2 /* SRCalloutView.swift */, 76 | 448B71D91FC0DECA005C69C2 /* SRCalloutView.xib */, 77 | 448B71DA1FC0DECA005C69C2 /* SRTriangleView.swift */, 78 | ); 79 | name = "Callout View"; 80 | sourceTree = ""; 81 | }; 82 | 448B71E51FC0DEFC005C69C2 /* Annotation View */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 448B71DF1FC0DEF9005C69C2 /* annotation_icon.png */, 86 | 448B71E01FC0DEF9005C69C2 /* SRAttraction.swift */, 87 | 448B71E11FC0DEF9005C69C2 /* SRAnnotationView.swift */, 88 | ); 89 | name = "Annotation View"; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXHeadersBuildPhase section */ 95 | 448B716A1FC0DD9F005C69C2 /* Headers */ = { 96 | isa = PBXHeadersBuildPhase; 97 | buildActionMask = 2147483647; 98 | files = ( 99 | ); 100 | runOnlyForDeploymentPostprocessing = 0; 101 | }; 102 | /* End PBXHeadersBuildPhase section */ 103 | 104 | /* Begin PBXNativeTarget section */ 105 | 448B716C1FC0DD9F005C69C2 /* SRAttractionsMap */ = { 106 | isa = PBXNativeTarget; 107 | buildConfigurationList = 448B71751FC0DD9F005C69C2 /* Build configuration list for PBXNativeTarget "SRAttractionsMap" */; 108 | buildPhases = ( 109 | 448B71681FC0DD9F005C69C2 /* Sources */, 110 | 448B71691FC0DD9F005C69C2 /* Frameworks */, 111 | 448B716A1FC0DD9F005C69C2 /* Headers */, 112 | 448B716B1FC0DD9F005C69C2 /* Resources */, 113 | ); 114 | buildRules = ( 115 | ); 116 | dependencies = ( 117 | ); 118 | name = SRAttractionsMap; 119 | productName = SRAttractionsMap; 120 | productReference = 448B716D1FC0DD9F005C69C2 /* SRAttractionsMap.framework */; 121 | productType = "com.apple.product-type.framework"; 122 | }; 123 | /* End PBXNativeTarget section */ 124 | 125 | /* Begin PBXProject section */ 126 | 448B71641FC0DD9F005C69C2 /* Project object */ = { 127 | isa = PBXProject; 128 | attributes = { 129 | LastUpgradeCheck = 0910; 130 | ORGANIZATIONNAME = "Ruslan Serebriakov"; 131 | TargetAttributes = { 132 | 448B716C1FC0DD9F005C69C2 = { 133 | CreatedOnToolsVersion = 8.3.3; 134 | DevelopmentTeam = 78L248PD54; 135 | LastSwiftMigration = 0910; 136 | ProvisioningStyle = Automatic; 137 | }; 138 | }; 139 | }; 140 | buildConfigurationList = 448B71671FC0DD9F005C69C2 /* Build configuration list for PBXProject "SRAttractionsMap" */; 141 | compatibilityVersion = "Xcode 3.2"; 142 | developmentRegion = English; 143 | hasScannedForEncodings = 0; 144 | knownRegions = ( 145 | en, 146 | ); 147 | mainGroup = 448B71631FC0DD9F005C69C2; 148 | productRefGroup = 448B716E1FC0DD9F005C69C2 /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 448B716C1FC0DD9F005C69C2 /* SRAttractionsMap */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 448B716B1FC0DD9F005C69C2 /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 448B71DC1FC0DECA005C69C2 /* SRCalloutView.xib in Resources */, 163 | 441B20A11FC5A1B4007A728E /* close_icon.png in Resources */, 164 | 448B71E21FC0DEF9005C69C2 /* annotation_icon.png in Resources */, 165 | ); 166 | runOnlyForDeploymentPostprocessing = 0; 167 | }; 168 | /* End PBXResourcesBuildPhase section */ 169 | 170 | /* Begin PBXSourcesBuildPhase section */ 171 | 448B71681FC0DD9F005C69C2 /* Sources */ = { 172 | isa = PBXSourcesBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 448B71DB1FC0DECA005C69C2 /* SRCalloutView.swift in Sources */, 176 | 448B71E31FC0DEF9005C69C2 /* SRAttraction.swift in Sources */, 177 | 448B71DD1FC0DECA005C69C2 /* SRTriangleView.swift in Sources */, 178 | 448B71B31FC0DDF6005C69C2 /* SRAttractionsMapViewController.swift in Sources */, 179 | 448B71E41FC0DEF9005C69C2 /* SRAnnotationView.swift in Sources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXSourcesBuildPhase section */ 184 | 185 | /* Begin XCBuildConfiguration section */ 186 | 448B71731FC0DD9F005C69C2 /* Debug */ = { 187 | isa = XCBuildConfiguration; 188 | buildSettings = { 189 | ALWAYS_SEARCH_USER_PATHS = NO; 190 | CLANG_ANALYZER_NONNULL = YES; 191 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 192 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 193 | CLANG_CXX_LIBRARY = "libc++"; 194 | CLANG_ENABLE_MODULES = YES; 195 | CLANG_ENABLE_OBJC_ARC = YES; 196 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 197 | CLANG_WARN_BOOL_CONVERSION = YES; 198 | CLANG_WARN_COMMA = YES; 199 | CLANG_WARN_CONSTANT_CONVERSION = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 209 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 210 | CLANG_WARN_STRICT_PROTOTYPES = YES; 211 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 212 | CLANG_WARN_UNREACHABLE_CODE = YES; 213 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 214 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 215 | COPY_PHASE_STRIP = NO; 216 | CURRENT_PROJECT_VERSION = 1; 217 | DEBUG_INFORMATION_FORMAT = dwarf; 218 | ENABLE_STRICT_OBJC_MSGSEND = YES; 219 | ENABLE_TESTABILITY = YES; 220 | GCC_C_LANGUAGE_STANDARD = gnu99; 221 | GCC_DYNAMIC_NO_PIC = NO; 222 | GCC_NO_COMMON_BLOCKS = YES; 223 | GCC_OPTIMIZATION_LEVEL = 0; 224 | GCC_PREPROCESSOR_DEFINITIONS = ( 225 | "DEBUG=1", 226 | "$(inherited)", 227 | ); 228 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 229 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 230 | GCC_WARN_UNDECLARED_SELECTOR = YES; 231 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 232 | GCC_WARN_UNUSED_FUNCTION = YES; 233 | GCC_WARN_UNUSED_VARIABLE = YES; 234 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 235 | MTL_ENABLE_DEBUG_INFO = YES; 236 | ONLY_ACTIVE_ARCH = YES; 237 | SDKROOT = iphoneos; 238 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 239 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 240 | TARGETED_DEVICE_FAMILY = "1,2"; 241 | VERSIONING_SYSTEM = "apple-generic"; 242 | VERSION_INFO_PREFIX = ""; 243 | }; 244 | name = Debug; 245 | }; 246 | 448B71741FC0DD9F005C69C2 /* Release */ = { 247 | isa = XCBuildConfiguration; 248 | buildSettings = { 249 | ALWAYS_SEARCH_USER_PATHS = NO; 250 | CLANG_ANALYZER_NONNULL = YES; 251 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 252 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 253 | CLANG_CXX_LIBRARY = "libc++"; 254 | CLANG_ENABLE_MODULES = YES; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_COMMA = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 261 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 262 | CLANG_WARN_EMPTY_BODY = YES; 263 | CLANG_WARN_ENUM_CONVERSION = YES; 264 | CLANG_WARN_INFINITE_RECURSION = YES; 265 | CLANG_WARN_INT_CONVERSION = YES; 266 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 267 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 269 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 270 | CLANG_WARN_STRICT_PROTOTYPES = YES; 271 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 275 | COPY_PHASE_STRIP = NO; 276 | CURRENT_PROJECT_VERSION = 1; 277 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 278 | ENABLE_NS_ASSERTIONS = NO; 279 | ENABLE_STRICT_OBJC_MSGSEND = YES; 280 | GCC_C_LANGUAGE_STANDARD = gnu99; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 289 | MTL_ENABLE_DEBUG_INFO = NO; 290 | SDKROOT = iphoneos; 291 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 292 | TARGETED_DEVICE_FAMILY = "1,2"; 293 | VALIDATE_PRODUCT = YES; 294 | VERSIONING_SYSTEM = "apple-generic"; 295 | VERSION_INFO_PREFIX = ""; 296 | }; 297 | name = Release; 298 | }; 299 | 448B71761FC0DD9F005C69C2 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | CLANG_ENABLE_MODULES = YES; 303 | CODE_SIGN_IDENTITY = ""; 304 | DEFINES_MODULE = YES; 305 | DEVELOPMENT_TEAM = 78L248PD54; 306 | DYLIB_COMPATIBILITY_VERSION = 1; 307 | DYLIB_CURRENT_VERSION = 1; 308 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 309 | INFOPLIST_FILE = SRAttractionsMap/Info.plist; 310 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 311 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 312 | PRODUCT_BUNDLE_IDENTIFIER = rsrbk.SRAttractionsMap; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | SKIP_INSTALL = YES; 315 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 316 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 317 | SWIFT_VERSION = 4.0; 318 | }; 319 | name = Debug; 320 | }; 321 | 448B71771FC0DD9F005C69C2 /* Release */ = { 322 | isa = XCBuildConfiguration; 323 | buildSettings = { 324 | CLANG_ENABLE_MODULES = YES; 325 | CODE_SIGN_IDENTITY = ""; 326 | DEFINES_MODULE = YES; 327 | DEVELOPMENT_TEAM = 78L248PD54; 328 | DYLIB_COMPATIBILITY_VERSION = 1; 329 | DYLIB_CURRENT_VERSION = 1; 330 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 331 | INFOPLIST_FILE = SRAttractionsMap/Info.plist; 332 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 333 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 334 | PRODUCT_BUNDLE_IDENTIFIER = rsrbk.SRAttractionsMap; 335 | PRODUCT_NAME = "$(TARGET_NAME)"; 336 | SKIP_INSTALL = YES; 337 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 338 | SWIFT_VERSION = 4.0; 339 | }; 340 | name = Release; 341 | }; 342 | /* End XCBuildConfiguration section */ 343 | 344 | /* Begin XCConfigurationList section */ 345 | 448B71671FC0DD9F005C69C2 /* Build configuration list for PBXProject "SRAttractionsMap" */ = { 346 | isa = XCConfigurationList; 347 | buildConfigurations = ( 348 | 448B71731FC0DD9F005C69C2 /* Debug */, 349 | 448B71741FC0DD9F005C69C2 /* Release */, 350 | ); 351 | defaultConfigurationIsVisible = 0; 352 | defaultConfigurationName = Release; 353 | }; 354 | 448B71751FC0DD9F005C69C2 /* Build configuration list for PBXNativeTarget "SRAttractionsMap" */ = { 355 | isa = XCConfigurationList; 356 | buildConfigurations = ( 357 | 448B71761FC0DD9F005C69C2 /* Debug */, 358 | 448B71771FC0DD9F005C69C2 /* Release */, 359 | ); 360 | defaultConfigurationIsVisible = 0; 361 | defaultConfigurationName = Release; 362 | }; 363 | /* End XCConfigurationList section */ 364 | }; 365 | rootObject = 448B71641FC0DD9F005C69C2 /* Project object */; 366 | } 367 | -------------------------------------------------------------------------------- /SRAttractionsMap.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SRAttractionsMap.xcodeproj/project.xcworkspace/xcuserdata/Ruslan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMap.xcodeproj/project.xcworkspace/xcuserdata/Ruslan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SRAttractionsMap.xcodeproj/xcuserdata/Ruslan.xcuserdatad/xcschemes/SRAttractionsMap.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /SRAttractionsMap.xcodeproj/xcuserdata/Ruslan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SRAttractionsMap.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 448B716C1FC0DD9F005C69C2 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SRAttractionsMap/Annotation View/SRAnnotationView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | import MapKit 22 | 23 | class SRAnnotationView: MKAnnotationView { 24 | 25 | init(annotationImage: UIImage, annotation: MKAnnotation?, reuseIdentifier: String?) { 26 | super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) 27 | 28 | canShowCallout = false 29 | image = annotationImage 30 | } 31 | 32 | required init?(coder aDecoder: NSCoder) { 33 | super.init(coder: aDecoder) 34 | } 35 | 36 | override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 37 | let hitView = super.hitTest(point, with: event) 38 | if hitView != nil { 39 | self.superview?.bringSubview(toFront: self) 40 | } 41 | 42 | return hitView 43 | } 44 | 45 | override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { 46 | var isInside = bounds.contains(point) 47 | if !isInside { 48 | for view in subviews { 49 | isInside = view.frame.contains(point) 50 | if isInside { 51 | break 52 | } 53 | } 54 | } 55 | 56 | return isInside 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SRAttractionsMap/Annotation View/SRAttraction.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | import MapKit 22 | 23 | public class SRAttraction: NSObject, MKAnnotation { 24 | internal var location: CLLocation { 25 | return CLLocation(latitude: latitude, longitude: longitude) 26 | } 27 | public var coordinate: CLLocationCoordinate2D { 28 | return location.coordinate 29 | } 30 | 31 | public var name: String? 32 | public var subname: String? 33 | public var image: UIImage? 34 | 35 | // This property provides you with the abillity to use 3d part framework(for ex. Kingfisher) 36 | public var imageDisplayingAction: ((UIImageView) -> Void)? 37 | 38 | /// Called when user has clicked on the "View more" button 39 | public var detailAction: ((_ currentViewController: UIViewController?) -> Void)? 40 | 41 | internal var latitude: Double 42 | internal var longitude: Double 43 | 44 | public init(latitude: Double, longitude: Double) { 45 | self.latitude = latitude 46 | self.longitude = longitude 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SRAttractionsMap/Annotation View/annotation_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMap/Annotation View/annotation_icon.png -------------------------------------------------------------------------------- /SRAttractionsMap/Callout View/SRCalloutView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | @IBOutlet weak var closeButton: UIButton! 18 | @IBOutlet weak var detailButtonHeight: NSLayoutConstraint! 19 | @IBOutlet weak var detailButtonHeight: NSLayoutConstraint! 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | import UIKit 25 | 26 | class SRCalloutView: UIView { 27 | @IBOutlet var contentView: UIView! 28 | @IBOutlet weak var imageView: UIImageView! 29 | @IBOutlet weak var titleLabel: UILabel! 30 | @IBOutlet weak var subtitleLabel: UILabel! 31 | @IBOutlet weak var detailButtonSeparator: UIView! 32 | @IBOutlet weak var detailButton: UIButton! 33 | @IBOutlet weak var detailButtonHeight: NSLayoutConstraint! 34 | @IBOutlet weak var closeButton: UIButton! 35 | 36 | var onCloseTap: (() -> Void)? 37 | var onDetailTap: (() -> Void)? 38 | 39 | override init(frame: CGRect) { 40 | super.init(frame: frame) 41 | commonInit() 42 | } 43 | 44 | required init?(coder aDecoder: NSCoder) { 45 | super.init(coder: aDecoder) 46 | commonInit() 47 | } 48 | 49 | func hideDetailButton() { 50 | detailButtonHeight.constant = 0 51 | detailButtonSeparator.backgroundColor = UIColor.clear 52 | layoutIfNeeded() 53 | } 54 | 55 | private func commonInit() { 56 | let frameworkBundle = Bundle.init(for: type(of: self)) 57 | if let view = frameworkBundle.loadNibNamed("SRCalloutView", owner: self, options: nil)?.first as? UIView { 58 | addSubview(view) 59 | view.frame = bounds 60 | 61 | view.layer.shadowOffset = CGSize(width: 0, height: 7) 62 | view.layer.shadowRadius = 5 63 | view.layer.shadowOpacity = 0.5 64 | 65 | closeButton.layer.cornerRadius = closeButton.frame.width / 2 66 | } 67 | } 68 | 69 | @IBAction func detailAction(_ sender: UIButton) { 70 | onDetailTap?() 71 | } 72 | 73 | @IBAction func closeAction(_ sender: UIButton) { 74 | onCloseTap?() 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /SRAttractionsMap/Callout View/SRCalloutView.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 42 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /SRAttractionsMap/Callout View/SRTriangleView.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | import UIKit 22 | 23 | class SRTriangleView: UIView { 24 | 25 | let shapeLayer = CAShapeLayer() 26 | 27 | override func draw(_ rect: CGRect) { 28 | 29 | // Get Height and Width 30 | let layerHeight = self.layer.frame.height 31 | let layerWidth = self.layer.frame.width 32 | 33 | // Create Path 34 | let bezierPath = UIBezierPath() 35 | 36 | // Draw Points 37 | bezierPath.move(to: CGPoint(x: 0, y: 0)) 38 | bezierPath.addLine(to: CGPoint(x: layerWidth, y: 0)) 39 | bezierPath.addLine(to: CGPoint(x: layerWidth / 2, y: layerHeight)) 40 | bezierPath.addLine(to: CGPoint(x: 0, y: 0)) 41 | bezierPath.close() 42 | 43 | // Apply Color 44 | UIColor.white.setFill() 45 | bezierPath.fill() 46 | 47 | // Mask to Path 48 | 49 | shapeLayer.path = bezierPath.cgPath 50 | self.layer.mask = shapeLayer 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SRAttractionsMap/Callout View/close_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMap/Callout View/close_icon.png -------------------------------------------------------------------------------- /SRAttractionsMap/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /SRAttractionsMap/SRAttractionsMapViewController.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | import MapKit 22 | 23 | /// Defines the level of the initial zoom of the map 24 | public enum SRAttractionMapDisplayMode { 25 | /// All attractions will be shown on the map 26 | case allAttractions 27 | /// The first location as the initial point and the radius is the distance to the second attraction times zoomRadiusMultiplier 28 | case firstTwoAttractions 29 | /// The user's location as the initial point and the radius is the distance to the first attraction times zoomRadiusMultiplier 30 | case userAndFirstAttraction 31 | /// The user's location as the initial point and the radius is the distance to the closest attraction times zoomRadiusMultiplier 32 | case userAndTheClosestLocation 33 | } 34 | 35 | open class SRAttractionsMapViewController: UIViewController { 36 | 37 | /// Attractions to show 38 | public var attractions: [SRAttraction] = [] { 39 | didSet { 40 | mapView.removeAnnotations(oldValue) 41 | mapView.addAnnotations(attractions) 42 | zoomInAttractions() 43 | } 44 | } 45 | 46 | /// Decides how the map gonna be zoomed in initially after appearing of the view controller 47 | public var displayMode: SRAttractionMapDisplayMode = .allAttractions 48 | /// The multiplier radius of initial zooming for display modes 49 | /// firstTwoAttractions/userAndFirstAttraction/userAndTheClosestLocation 50 | public var zoomRadiusMultiplier: Double = 2 51 | /// If this theshold in the mode .userAndTheClosestLocation exceeds 52 | /// - the map will be switched to the .allAttractions mode 53 | public var closestDistanceThreshold: CLLocationDistance = 10000 54 | 55 | /// Animation duration. Set to nil if you don't want to have the animation 56 | public var calloutFadeInAnimationDuration: TimeInterval? = 0.25 57 | /// The size of the callout view which shows when a pin is selected 58 | public var calloutViewSize: CGSize! 59 | 60 | /// Font for the title of an attraction 61 | public var calloutTitleFont: UIFont? 62 | /// Text color for the title of an attraction 63 | public var calloutTitleColor: UIColor? 64 | 65 | /// Font for the title of an attraction 66 | public var calloutSubtitleFont: UIFont? 67 | /// Text color for the title of an attraction 68 | public var calloutSubtitleColor: UIColor? 69 | 70 | /// Font for the text on the CTA button on the callout view 71 | public var calloutDetailButtonFont: UIFont? 72 | /// Text color for the CTA button on the callout view 73 | public var calloutDetailButtonTextColor: UIColor? 74 | /// Text to show on the CTA button on the callout view 75 | public var calloutDetailButtonTitle: String? 76 | 77 | /// Moves a selected pin to the center if true 78 | public var shouldScrollToPin = true 79 | /// Custom marker for attractions on the map 80 | public var customPinImage: UIImage? 81 | 82 | public init(attractions: [SRAttraction], displayMode: SRAttractionMapDisplayMode) { 83 | super.init(nibName: nil, bundle: nil) 84 | 85 | self.attractions = attractions 86 | self.displayMode = displayMode 87 | } 88 | 89 | convenience public init(customPinImage: UIImage, attractions: [SRAttraction], displayMode: SRAttractionMapDisplayMode) { 90 | self.init(attractions: attractions, displayMode: displayMode) 91 | 92 | self.customPinImage = customPinImage 93 | } 94 | 95 | // Internal stuff 96 | required public init?(coder aDecoder: NSCoder) { 97 | super.init(coder: aDecoder) 98 | } 99 | 100 | override open func viewDidLoad() { 101 | super.viewDidLoad() 102 | 103 | view.addSubview(mapView) 104 | mapView.frame = view.bounds 105 | mapView.showsUserLocation = true 106 | mapView.delegate = self 107 | mapView.addAnnotations(attractions) 108 | 109 | zoomInAttractions() 110 | } 111 | 112 | public func zoomInAttractions() { 113 | switch displayMode { 114 | case .userAndFirstAttraction, .userAndTheClosestLocation: 115 | locationManager.delegate = self 116 | locationManager.desiredAccuracy = kCLLocationAccuracyBest 117 | locationManager.requestWhenInUseAuthorization() 118 | case .firstTwoAttractions: 119 | if attractions.count < 2 { 120 | fatalError("We need to have at least 2 attractions for this mode") 121 | } 122 | 123 | let first = attractions[0] 124 | let second = attractions[1] 125 | let radius: CLLocationDistance = first.location.distance(from: second.location) * zoomRadiusMultiplier 126 | let region = MKCoordinateRegionMakeWithDistance(first.coordinate, radius, radius) 127 | mapView.setRegion(region, animated: true) 128 | case .allAttractions: 129 | mapView.showAnnotations(attractions, animated: true) 130 | } 131 | } 132 | 133 | internal var locationManager = CLLocationManager() 134 | internal var mapView = MKMapView() 135 | internal var calloutView: SRCalloutView? 136 | 137 | internal var zoomedInInitially = false 138 | } 139 | 140 | extension SRAttractionsMapViewController: CLLocationManagerDelegate { 141 | public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { 142 | switch status { 143 | case .authorizedWhenInUse: 144 | locationManager.startUpdatingLocation() 145 | case .notDetermined: 146 | locationManager.requestWhenInUseAuthorization() 147 | case .denied, .restricted: 148 | displayMode = .allAttractions 149 | mapView.showAnnotations(attractions, animated: true) 150 | default: 151 | break 152 | } 153 | } 154 | 155 | public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { 156 | if zoomedInInitially || attractions.isEmpty { 157 | return 158 | } 159 | 160 | if let location = locations.last { 161 | switch displayMode { 162 | case .userAndFirstAttraction: 163 | if let attraction = attractions.first { 164 | let radius: CLLocationDistance = location.distance(from: attraction.location) * zoomRadiusMultiplier 165 | let region = MKCoordinateRegionMakeWithDistance(location.coordinate, radius, radius) 166 | mapView.setRegion(region, animated: true) 167 | } 168 | case .userAndTheClosestLocation: 169 | var closestDistance: CLLocationDistance = Double(Int.max) 170 | for attraction in attractions { 171 | let currentDistance = location.distance(from: attraction.location) 172 | if currentDistance < closestDistance { 173 | closestDistance = currentDistance 174 | } 175 | } 176 | 177 | if closestDistance < closestDistanceThreshold { 178 | let radius = closestDistance * zoomRadiusMultiplier 179 | let region = MKCoordinateRegionMakeWithDistance(location.coordinate, radius, radius) 180 | mapView.setRegion(region, animated: true) 181 | } else { 182 | displayMode = .allAttractions 183 | mapView.showAnnotations(attractions, animated: true) 184 | } 185 | case .firstTwoAttractions, .allAttractions: 186 | break 187 | } 188 | 189 | zoomedInInitially = true 190 | } 191 | } 192 | 193 | public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { 194 | let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.alert) 195 | alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil)) 196 | 197 | self.present(alert, animated: true, completion: nil) 198 | } 199 | } 200 | 201 | extension SRAttractionsMapViewController: MKMapViewDelegate { 202 | public func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { 203 | if annotation is MKUserLocation { 204 | return nil 205 | } 206 | 207 | let reuseIdentifier = "annotation" 208 | var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) 209 | if annotationView == nil { 210 | var pinImage = customPinImage 211 | 212 | if pinImage == nil { 213 | let frameworkBundle = Bundle.init(for: type(of: self)) 214 | pinImage = UIImage(named: "annotation_icon", in: frameworkBundle, compatibleWith: nil) 215 | } 216 | 217 | annotationView = SRAnnotationView(annotationImage: pinImage!, 218 | annotation: annotation, 219 | reuseIdentifier: reuseIdentifier) 220 | annotationView?.centerOffset = CGPoint(x: 0, y: -pinImage!.size.height / 2) 221 | } 222 | 223 | return annotationView 224 | } 225 | 226 | public func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) { 227 | if let attraction = view.annotation as? SRAttraction { 228 | if calloutView != nil { 229 | mapView.deselectAnnotation(attraction, animated: false) 230 | } 231 | 232 | if (calloutViewSize == nil) { 233 | setDefaultCalloutViewSize() 234 | } 235 | let newCalloutView = generateCalloutView(attraction: attraction) 236 | let verticalOffset: CGFloat = 4 237 | newCalloutView.frame = CGRect(x: view.frame.width/2 - calloutViewSize.width/2, 238 | y: -calloutViewSize.height - verticalOffset, 239 | width: calloutViewSize.width, 240 | height: calloutViewSize.height) 241 | view.addSubview(newCalloutView) 242 | 243 | if shouldScrollToPin { 244 | mapView.setCenter(attraction.coordinate, animated: true) 245 | } 246 | 247 | // Fade in animation 248 | if let duration = calloutFadeInAnimationDuration { 249 | newCalloutView.alpha = 0 250 | UIView.animate(withDuration: duration, animations: { 251 | newCalloutView.alpha = 1 252 | }) 253 | } 254 | 255 | calloutView = newCalloutView 256 | } 257 | } 258 | 259 | public func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) { 260 | if let calloutView = calloutView { 261 | self.calloutView = nil 262 | 263 | if let duration = calloutFadeInAnimationDuration { 264 | UIView.animate(withDuration: duration, animations: { 265 | calloutView.alpha = 0 266 | }, completion: { _ in 267 | calloutView.removeFromSuperview() 268 | }) 269 | } else { 270 | calloutView.removeFromSuperview() 271 | } 272 | } 273 | } 274 | 275 | private func setDefaultCalloutViewSize() { 276 | if UIDevice.current.model.contains("iPad") { 277 | // iPad 278 | calloutViewSize = CGSize(width: 350, height: 300) 279 | } else if UIScreen.main.nativeBounds.height == 2436 { 280 | // iPhone X 281 | calloutViewSize = CGSize(width: UIScreen.main.bounds.width-20, height: UIScreen.main.bounds.height/2-120) 282 | } else if UIScreen.main.nativeBounds.height == 2208 { 283 | // iPhone 6+/6S+/7+/8+ 284 | calloutViewSize = CGSize(width: UIScreen.main.bounds.width-20, height: UIScreen.main.bounds.height/2-80) 285 | } else { 286 | calloutViewSize = CGSize(width: UIScreen.main.bounds.width-20, height: UIScreen.main.bounds.height/2-40) 287 | } 288 | } 289 | 290 | private func generateCalloutView(attraction: SRAttraction) -> SRCalloutView { 291 | let calloutView = SRCalloutView(frame: .zero) 292 | if attraction.image != nil { 293 | calloutView.imageView.image = attraction.image 294 | } else { 295 | attraction.imageDisplayingAction?(calloutView.imageView) 296 | } 297 | 298 | calloutView.titleLabel.text = attraction.name 299 | if let titleFont = calloutTitleFont { 300 | calloutView.titleLabel.font = titleFont 301 | } 302 | if let titleColor = calloutTitleColor { 303 | calloutView.titleLabel.textColor = titleColor 304 | } 305 | 306 | calloutView.subtitleLabel.text = attraction.subname 307 | if let subtitleFont = calloutSubtitleFont { 308 | calloutView.subtitleLabel.font = subtitleFont 309 | } 310 | if let subtitleColor = calloutSubtitleColor { 311 | calloutView.subtitleLabel.textColor = subtitleColor 312 | } 313 | 314 | if let buttonTitle = calloutDetailButtonTitle { 315 | calloutView.detailButton.setTitle(buttonTitle, for: .normal) 316 | calloutView.onDetailTap = { [weak self] in 317 | attraction.detailAction?(self) 318 | } 319 | 320 | if let buttonFont = calloutDetailButtonFont { 321 | calloutView.detailButton.titleLabel?.font = buttonFont 322 | } 323 | if let buttonTextColor = calloutDetailButtonTextColor { 324 | calloutView.detailButton.setTitleColor(buttonTextColor, for: .normal) 325 | } 326 | } else { 327 | calloutView.hideDetailButton() 328 | } 329 | 330 | calloutView.onDetailTap = { [weak self] in 331 | attraction.detailAction?(self) 332 | } 333 | calloutView.onCloseTap = { [weak self] in 334 | self?.mapView.deselectAnnotation(attraction, animated: true) 335 | } 336 | 337 | return calloutView 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 44E2E4531FC1BF3400F34789 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E2E4521FC1BF3400F34789 /* AppDelegate.swift */; }; 11 | 44E2E4551FC1BF3400F34789 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44E2E4541FC1BF3400F34789 /* ViewController.swift */; }; 12 | 44E2E4581FC1BF3400F34789 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44E2E4561FC1BF3400F34789 /* Main.storyboard */; }; 13 | 44E2E45A1FC1BF3400F34789 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 44E2E4591FC1BF3400F34789 /* Assets.xcassets */; }; 14 | 44E2E45D1FC1BF3400F34789 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 44E2E45B1FC1BF3400F34789 /* LaunchScreen.storyboard */; }; 15 | 44E750F01FC1BF9500524706 /* SRAttractionsMap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44E2E4691FC1BF4900F34789 /* SRAttractionsMap.framework */; }; 16 | 44E750F11FC1BF9500524706 /* SRAttractionsMap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 44E2E4691FC1BF4900F34789 /* SRAttractionsMap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 44E750F71FC1C00600524706 /* CoffeeshopsList.plist in Resources */ = {isa = PBXBuildFile; fileRef = 44E750F61FC1C00600524706 /* CoffeeshopsList.plist */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXContainerItemProxy section */ 21 | 44E2E4681FC1BF4900F34789 /* PBXContainerItemProxy */ = { 22 | isa = PBXContainerItemProxy; 23 | containerPortal = 44E2E4641FC1BF4900F34789 /* SRAttractionsMap.xcodeproj */; 24 | proxyType = 2; 25 | remoteGlobalIDString = 448B716D1FC0DD9F005C69C2; 26 | remoteInfo = SRAttractionsMap; 27 | }; 28 | 44E750F21FC1BF9500524706 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 44E2E4641FC1BF4900F34789 /* SRAttractionsMap.xcodeproj */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 448B716C1FC0DD9F005C69C2; 33 | remoteInfo = SRAttractionsMap; 34 | }; 35 | /* End PBXContainerItemProxy section */ 36 | 37 | /* Begin PBXCopyFilesBuildPhase section */ 38 | 44E750F41FC1BF9500524706 /* Embed Frameworks */ = { 39 | isa = PBXCopyFilesBuildPhase; 40 | buildActionMask = 2147483647; 41 | dstPath = ""; 42 | dstSubfolderSpec = 10; 43 | files = ( 44 | 44E750F11FC1BF9500524706 /* SRAttractionsMap.framework in Embed Frameworks */, 45 | ); 46 | name = "Embed Frameworks"; 47 | runOnlyForDeploymentPostprocessing = 0; 48 | }; 49 | /* End PBXCopyFilesBuildPhase section */ 50 | 51 | /* Begin PBXFileReference section */ 52 | 44E2E44F1FC1BF3400F34789 /* SRAttractionsMapExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SRAttractionsMapExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 44E2E4521FC1BF3400F34789 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 54 | 44E2E4541FC1BF3400F34789 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 55 | 44E2E4571FC1BF3400F34789 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | 44E2E4591FC1BF3400F34789 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 57 | 44E2E45C1FC1BF3400F34789 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 58 | 44E2E45E1FC1BF3400F34789 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 44E2E4641FC1BF4900F34789 /* SRAttractionsMap.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SRAttractionsMap.xcodeproj; path = ../SRAttractionsMap.xcodeproj; sourceTree = ""; }; 60 | 44E750F61FC1C00600524706 /* CoffeeshopsList.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CoffeeshopsList.plist; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 44E2E44C1FC1BF3400F34789 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | 44E750F01FC1BF9500524706 /* SRAttractionsMap.framework in Frameworks */, 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | /* End PBXFrameworksBuildPhase section */ 73 | 74 | /* Begin PBXGroup section */ 75 | 44E2E4461FC1BF3400F34789 = { 76 | isa = PBXGroup; 77 | children = ( 78 | 44E2E4641FC1BF4900F34789 /* SRAttractionsMap.xcodeproj */, 79 | 44E2E4511FC1BF3400F34789 /* SRAttractionsMapExample */, 80 | 44E2E4501FC1BF3400F34789 /* Products */, 81 | ); 82 | sourceTree = ""; 83 | }; 84 | 44E2E4501FC1BF3400F34789 /* Products */ = { 85 | isa = PBXGroup; 86 | children = ( 87 | 44E2E44F1FC1BF3400F34789 /* SRAttractionsMapExample.app */, 88 | ); 89 | name = Products; 90 | sourceTree = ""; 91 | }; 92 | 44E2E4511FC1BF3400F34789 /* SRAttractionsMapExample */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | 44E2E4521FC1BF3400F34789 /* AppDelegate.swift */, 96 | 44E2E4541FC1BF3400F34789 /* ViewController.swift */, 97 | 44E750F61FC1C00600524706 /* CoffeeshopsList.plist */, 98 | 44E2E4561FC1BF3400F34789 /* Main.storyboard */, 99 | 44E2E4591FC1BF3400F34789 /* Assets.xcassets */, 100 | 44E2E45B1FC1BF3400F34789 /* LaunchScreen.storyboard */, 101 | 44E2E45E1FC1BF3400F34789 /* Info.plist */, 102 | ); 103 | path = SRAttractionsMapExample; 104 | sourceTree = ""; 105 | }; 106 | 44E2E4651FC1BF4900F34789 /* Products */ = { 107 | isa = PBXGroup; 108 | children = ( 109 | 44E2E4691FC1BF4900F34789 /* SRAttractionsMap.framework */, 110 | ); 111 | name = Products; 112 | sourceTree = ""; 113 | }; 114 | /* End PBXGroup section */ 115 | 116 | /* Begin PBXNativeTarget section */ 117 | 44E2E44E1FC1BF3400F34789 /* SRAttractionsMapExample */ = { 118 | isa = PBXNativeTarget; 119 | buildConfigurationList = 44E2E4611FC1BF3400F34789 /* Build configuration list for PBXNativeTarget "SRAttractionsMapExample" */; 120 | buildPhases = ( 121 | 44E2E44B1FC1BF3400F34789 /* Sources */, 122 | 44E2E44C1FC1BF3400F34789 /* Frameworks */, 123 | 44E2E44D1FC1BF3400F34789 /* Resources */, 124 | 44E750F41FC1BF9500524706 /* Embed Frameworks */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | 44E750F31FC1BF9500524706 /* PBXTargetDependency */, 130 | ); 131 | name = SRAttractionsMapExample; 132 | productName = SRAttractionsMapExample; 133 | productReference = 44E2E44F1FC1BF3400F34789 /* SRAttractionsMapExample.app */; 134 | productType = "com.apple.product-type.application"; 135 | }; 136 | /* End PBXNativeTarget section */ 137 | 138 | /* Begin PBXProject section */ 139 | 44E2E4471FC1BF3400F34789 /* Project object */ = { 140 | isa = PBXProject; 141 | attributes = { 142 | LastSwiftUpdateCheck = 0830; 143 | LastUpgradeCheck = 0910; 144 | ORGANIZATIONNAME = "Ruslan Serebriakov"; 145 | TargetAttributes = { 146 | 44E2E44E1FC1BF3400F34789 = { 147 | CreatedOnToolsVersion = 8.3.3; 148 | DevelopmentTeam = 6U6DEJ8A2S; 149 | LastSwiftMigration = 0910; 150 | ProvisioningStyle = Automatic; 151 | }; 152 | }; 153 | }; 154 | buildConfigurationList = 44E2E44A1FC1BF3400F34789 /* Build configuration list for PBXProject "SRAttractionsMapExample" */; 155 | compatibilityVersion = "Xcode 3.2"; 156 | developmentRegion = English; 157 | hasScannedForEncodings = 0; 158 | knownRegions = ( 159 | en, 160 | Base, 161 | ); 162 | mainGroup = 44E2E4461FC1BF3400F34789; 163 | productRefGroup = 44E2E4501FC1BF3400F34789 /* Products */; 164 | projectDirPath = ""; 165 | projectReferences = ( 166 | { 167 | ProductGroup = 44E2E4651FC1BF4900F34789 /* Products */; 168 | ProjectRef = 44E2E4641FC1BF4900F34789 /* SRAttractionsMap.xcodeproj */; 169 | }, 170 | ); 171 | projectRoot = ""; 172 | targets = ( 173 | 44E2E44E1FC1BF3400F34789 /* SRAttractionsMapExample */, 174 | ); 175 | }; 176 | /* End PBXProject section */ 177 | 178 | /* Begin PBXReferenceProxy section */ 179 | 44E2E4691FC1BF4900F34789 /* SRAttractionsMap.framework */ = { 180 | isa = PBXReferenceProxy; 181 | fileType = wrapper.framework; 182 | path = SRAttractionsMap.framework; 183 | remoteRef = 44E2E4681FC1BF4900F34789 /* PBXContainerItemProxy */; 184 | sourceTree = BUILT_PRODUCTS_DIR; 185 | }; 186 | /* End PBXReferenceProxy section */ 187 | 188 | /* Begin PBXResourcesBuildPhase section */ 189 | 44E2E44D1FC1BF3400F34789 /* Resources */ = { 190 | isa = PBXResourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 44E2E45D1FC1BF3400F34789 /* LaunchScreen.storyboard in Resources */, 194 | 44E2E45A1FC1BF3400F34789 /* Assets.xcassets in Resources */, 195 | 44E2E4581FC1BF3400F34789 /* Main.storyboard in Resources */, 196 | 44E750F71FC1C00600524706 /* CoffeeshopsList.plist in Resources */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | /* End PBXResourcesBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 44E2E44B1FC1BF3400F34789 /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 44E2E4551FC1BF3400F34789 /* ViewController.swift in Sources */, 208 | 44E2E4531FC1BF3400F34789 /* AppDelegate.swift in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXTargetDependency section */ 215 | 44E750F31FC1BF9500524706 /* PBXTargetDependency */ = { 216 | isa = PBXTargetDependency; 217 | name = SRAttractionsMap; 218 | targetProxy = 44E750F21FC1BF9500524706 /* PBXContainerItemProxy */; 219 | }; 220 | /* End PBXTargetDependency section */ 221 | 222 | /* Begin PBXVariantGroup section */ 223 | 44E2E4561FC1BF3400F34789 /* Main.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 44E2E4571FC1BF3400F34789 /* Base */, 227 | ); 228 | name = Main.storyboard; 229 | sourceTree = ""; 230 | }; 231 | 44E2E45B1FC1BF3400F34789 /* LaunchScreen.storyboard */ = { 232 | isa = PBXVariantGroup; 233 | children = ( 234 | 44E2E45C1FC1BF3400F34789 /* Base */, 235 | ); 236 | name = LaunchScreen.storyboard; 237 | sourceTree = ""; 238 | }; 239 | /* End PBXVariantGroup section */ 240 | 241 | /* Begin XCBuildConfiguration section */ 242 | 44E2E45F1FC1BF3400F34789 /* Debug */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_ANALYZER_NONNULL = YES; 247 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 249 | CLANG_CXX_LIBRARY = "libc++"; 250 | CLANG_ENABLE_MODULES = YES; 251 | CLANG_ENABLE_OBJC_ARC = YES; 252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_COMMA = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 257 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 258 | CLANG_WARN_EMPTY_BODY = YES; 259 | CLANG_WARN_ENUM_CONVERSION = YES; 260 | CLANG_WARN_INFINITE_RECURSION = YES; 261 | CLANG_WARN_INT_CONVERSION = YES; 262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 263 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 265 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 266 | CLANG_WARN_STRICT_PROTOTYPES = YES; 267 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 268 | CLANG_WARN_UNREACHABLE_CODE = YES; 269 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 270 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 271 | COPY_PHASE_STRIP = NO; 272 | DEBUG_INFORMATION_FORMAT = dwarf; 273 | ENABLE_STRICT_OBJC_MSGSEND = YES; 274 | ENABLE_TESTABILITY = YES; 275 | GCC_C_LANGUAGE_STANDARD = gnu99; 276 | GCC_DYNAMIC_NO_PIC = NO; 277 | GCC_NO_COMMON_BLOCKS = YES; 278 | GCC_OPTIMIZATION_LEVEL = 0; 279 | GCC_PREPROCESSOR_DEFINITIONS = ( 280 | "DEBUG=1", 281 | "$(inherited)", 282 | ); 283 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 284 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 285 | GCC_WARN_UNDECLARED_SELECTOR = YES; 286 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 287 | GCC_WARN_UNUSED_FUNCTION = YES; 288 | GCC_WARN_UNUSED_VARIABLE = YES; 289 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 290 | MTL_ENABLE_DEBUG_INFO = YES; 291 | ONLY_ACTIVE_ARCH = YES; 292 | SDKROOT = iphoneos; 293 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 294 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 295 | TARGETED_DEVICE_FAMILY = "1,2"; 296 | }; 297 | name = Debug; 298 | }; 299 | 44E2E4601FC1BF3400F34789 /* Release */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ALWAYS_SEARCH_USER_PATHS = NO; 303 | CLANG_ANALYZER_NONNULL = YES; 304 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 305 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 306 | CLANG_CXX_LIBRARY = "libc++"; 307 | CLANG_ENABLE_MODULES = YES; 308 | CLANG_ENABLE_OBJC_ARC = YES; 309 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 310 | CLANG_WARN_BOOL_CONVERSION = YES; 311 | CLANG_WARN_COMMA = YES; 312 | CLANG_WARN_CONSTANT_CONVERSION = YES; 313 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 314 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 315 | CLANG_WARN_EMPTY_BODY = YES; 316 | CLANG_WARN_ENUM_CONVERSION = YES; 317 | CLANG_WARN_INFINITE_RECURSION = YES; 318 | CLANG_WARN_INT_CONVERSION = YES; 319 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 320 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 321 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 322 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 323 | CLANG_WARN_STRICT_PROTOTYPES = YES; 324 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 325 | CLANG_WARN_UNREACHABLE_CODE = YES; 326 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 327 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 328 | COPY_PHASE_STRIP = NO; 329 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 330 | ENABLE_NS_ASSERTIONS = NO; 331 | ENABLE_STRICT_OBJC_MSGSEND = YES; 332 | GCC_C_LANGUAGE_STANDARD = gnu99; 333 | GCC_NO_COMMON_BLOCKS = YES; 334 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 335 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 336 | GCC_WARN_UNDECLARED_SELECTOR = YES; 337 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 338 | GCC_WARN_UNUSED_FUNCTION = YES; 339 | GCC_WARN_UNUSED_VARIABLE = YES; 340 | IPHONEOS_DEPLOYMENT_TARGET = 10.3; 341 | MTL_ENABLE_DEBUG_INFO = NO; 342 | SDKROOT = iphoneos; 343 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 344 | TARGETED_DEVICE_FAMILY = "1,2"; 345 | VALIDATE_PRODUCT = YES; 346 | }; 347 | name = Release; 348 | }; 349 | 44E2E4621FC1BF3400F34789 /* Debug */ = { 350 | isa = XCBuildConfiguration; 351 | buildSettings = { 352 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 353 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 354 | DEVELOPMENT_TEAM = 6U6DEJ8A2S; 355 | INFOPLIST_FILE = SRAttractionsMapExample/Info.plist; 356 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 357 | PRODUCT_BUNDLE_IDENTIFIER = rsrbk.SRAttractionsMapExample; 358 | PRODUCT_NAME = "$(TARGET_NAME)"; 359 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 360 | SWIFT_VERSION = 4.0; 361 | }; 362 | name = Debug; 363 | }; 364 | 44E2E4631FC1BF3400F34789 /* Release */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 368 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 369 | DEVELOPMENT_TEAM = 6U6DEJ8A2S; 370 | INFOPLIST_FILE = SRAttractionsMapExample/Info.plist; 371 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 372 | PRODUCT_BUNDLE_IDENTIFIER = rsrbk.SRAttractionsMapExample; 373 | PRODUCT_NAME = "$(TARGET_NAME)"; 374 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 375 | SWIFT_VERSION = 4.0; 376 | }; 377 | name = Release; 378 | }; 379 | /* End XCBuildConfiguration section */ 380 | 381 | /* Begin XCConfigurationList section */ 382 | 44E2E44A1FC1BF3400F34789 /* Build configuration list for PBXProject "SRAttractionsMapExample" */ = { 383 | isa = XCConfigurationList; 384 | buildConfigurations = ( 385 | 44E2E45F1FC1BF3400F34789 /* Debug */, 386 | 44E2E4601FC1BF3400F34789 /* Release */, 387 | ); 388 | defaultConfigurationIsVisible = 0; 389 | defaultConfigurationName = Release; 390 | }; 391 | 44E2E4611FC1BF3400F34789 /* Build configuration list for PBXNativeTarget "SRAttractionsMapExample" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | 44E2E4621FC1BF3400F34789 /* Debug */, 395 | 44E2E4631FC1BF3400F34789 /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | /* End XCConfigurationList section */ 401 | }; 402 | rootObject = 44E2E4471FC1BF3400F34789 /* Project object */; 403 | } 404 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/project.xcworkspace/xcuserdata/Ruslan.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/project.xcworkspace/xcuserdata/Ruslan.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/xcuserdata/Ruslan.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/xcuserdata/Ruslan.xcuserdatad/xcschemes/SRAttractionsMapExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample.xcodeproj/xcuserdata/Ruslan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SRAttractionsMapExample.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 44E2E44E1FC1BF3400F34789 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | import UIKit 22 | 23 | @UIApplicationMain 24 | class AppDelegate: UIResponder, UIApplicationDelegate { 25 | 26 | var window: UIWindow? 27 | 28 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 29 | // Override point for customization after application launch. 30 | return true 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/1.imageset/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/1.imageset/1.png -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "1.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/2.imageset/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/2.imageset/2.png -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "2.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/3.imageset/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/3.imageset/3.png -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "3.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/4.imageset/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/4.imageset/4.png -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "4.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/CoffeeshopsList.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title 7 | Green House Centrum 8 | subtitle 9 | Relaxing place right in the heart of the town 10 | latitude 11 | 52.371464 12 | longitude 13 | 4.895826 14 | 15 | 16 | title 17 | Kadinsky Coffeeshop 18 | subtitle 19 | One of the oldest coffeeshops in Amsterdam 20 | latitude 21 | 52.374244 22 | longitude 23 | 4.893463 24 | 25 | 26 | title 27 | Coffeeshop Siberia 28 | subtitle 29 | Quality and Excellence, since 1984 30 | latitude 31 | 52.378867 32 | longitude 33 | 4.892930 34 | 35 | 36 | title 37 | Boerejongens Coffeeshop West 38 | subtitle 39 | The Cannabis Sommeliers of Amsterdam 40 | latitude 41 | 52.362315 42 | longitude 43 | 4.857548 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UIStatusBarStyle 32 | UIStatusBarStyleLightContent 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UIViewControllerBasedStatusBarAppearance 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /SRAttractionsMapExample/SRAttractionsMapExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | Copyright (c) 2017 Ruslan Serebriakov 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 17 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 18 | SOFTWARE. 19 | */ 20 | 21 | import UIKit 22 | import MapKit 23 | import SRAttractionsMap 24 | 25 | class ViewController: UIViewController { 26 | 27 | override func viewDidAppear(_ animated: Bool) { 28 | super.viewDidAppear(animated) 29 | 30 | if let path = Bundle.main.path(forResource: "CoffeeshopsList", ofType: "plist") { 31 | if let array = NSArray.init(contentsOfFile: path) { 32 | let attractions = array.enumerated().flatMap { (index, element) -> SRAttraction? in 33 | guard let dictionary = element as? NSDictionary else { return nil } 34 | guard let title = dictionary["title"] as? String else { return nil } 35 | guard let subtitle = dictionary["subtitle"] as? String else { return nil } 36 | guard let latitudeString = dictionary["latitude"] as? String else { return nil } 37 | guard let longitudeString = dictionary["longitude"] as? String else { return nil } 38 | guard let image = UIImage(named: "\(index + 1)") else { return nil } 39 | guard let latitude = Double(latitudeString) else { return nil } 40 | guard let longitude = Double(longitudeString) else { return nil } 41 | 42 | let attraction = SRAttraction(latitude: latitude, longitude: longitude) 43 | attraction.name = title 44 | attraction.subname = subtitle 45 | attraction.image = image 46 | attraction.detailAction = { _ in 47 | let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: attraction.coordinate, addressDictionary:nil)) 48 | mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeWalking]) 49 | } 50 | 51 | return attraction 52 | } 53 | 54 | 55 | let mapVC = SRAttractionsMapViewController(attractions: attractions, displayMode: .allAttractions) 56 | mapVC.title = "Amsterdam Coffeshops" 57 | mapVC.calloutDetailButtonTitle = "View directions" 58 | 59 | let nVC = UINavigationController(rootViewController: mapVC) 60 | nVC.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17.0, weight: .light), 61 | NSAttributedStringKey.foregroundColor: UIColor.white] 62 | nVC.navigationBar.barTintColor = UIColor(red: 52.0/255.0, green: 52.0/255.0, blue: 52.0/255.0, alpha: 1.0) 63 | nVC.navigationBar.tintColor = UIColor.white 64 | nVC.navigationBar.isTranslucent = false 65 | self.present(nVC, animated: false, completion: nil) 66 | } 67 | } 68 | } 69 | } 70 | 71 | -------------------------------------------------------------------------------- /gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rsrbk/SRAttractionsMap/b89678ac10f864bd8f08abb4dc94e2182fa2094c/gif.gif --------------------------------------------------------------------------------