├── .gitignore ├── CalloutDemo ├── CalloutDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── CalloutDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets │ │ ├── bluestar.png │ │ ├── bluestar@2x.png │ │ ├── redstar.png │ │ └── redstar@2x.png │ ├── CalloutDemo-Info.plist │ ├── CalloutDemo-Prefix.pch │ ├── District.h │ ├── District.m │ ├── Representative.h │ ├── Representative.m │ ├── ViewController.h │ ├── ViewController.m │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ ├── ViewController_iPad.xib │ │ └── ViewController_iPhone.xib │ └── main.m └── Default-568h@2x.png ├── LICENSE.html ├── MultiRowCalloutAnnotationView ├── GenericPinAnnotationView.h ├── GenericPinAnnotationView.m ├── MultiRowAnnotation.h ├── MultiRowAnnotation.m ├── MultiRowAnnotationProtocol.h ├── MultiRowCalloutAnnotationView.h ├── MultiRowCalloutAnnotationView.m ├── MultiRowCalloutCell.h └── MultiRowCalloutCell.m ├── README.md └── screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.mode1v3 3 | *.pbxuser 4 | xcuserdata 5 | 6 | CalloutDemo/CalloutDemo.xcodeproj/project.xcworkspace/xcuserdata 7 | CalloutDemo/CalloutDemo.xcodeproj/xcuserdata -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 372F011C1A1DA8020075D7A5 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 372F011B1A1DA8020075D7A5 /* Default-568h@2x.png */; }; 11 | 3770F11F1486DD2E00FD854B /* bluestar.png in Resources */ = {isa = PBXBuildFile; fileRef = 3770F11B1486DD2E00FD854B /* bluestar.png */; }; 12 | 3770F1201486DD2E00FD854B /* bluestar@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3770F11C1486DD2E00FD854B /* bluestar@2x.png */; }; 13 | 3770F1211486DD2E00FD854B /* redstar.png in Resources */ = {isa = PBXBuildFile; fileRef = 3770F11D1486DD2E00FD854B /* redstar.png */; }; 14 | 3770F1221486DD2E00FD854B /* redstar@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 3770F11E1486DD2E00FD854B /* redstar@2x.png */; }; 15 | 3770F14014875BB600FD854B /* MultiRowAnnotation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3770F13E14875BB600FD854B /* MultiRowAnnotation.m */; }; 16 | 3780FB251486904F00CAA79B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780FB241486904F00CAA79B /* UIKit.framework */; }; 17 | 3780FB271486904F00CAA79B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780FB261486904F00CAA79B /* Foundation.framework */; }; 18 | 3780FB291486904F00CAA79B /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780FB281486904F00CAA79B /* CoreGraphics.framework */; }; 19 | 3780FB2F1486904F00CAA79B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 3780FB2D1486904F00CAA79B /* InfoPlist.strings */; }; 20 | 3780FB311486904F00CAA79B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB301486904F00CAA79B /* main.m */; }; 21 | 3780FB351486904F00CAA79B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB341486904F00CAA79B /* AppDelegate.m */; }; 22 | 3780FB381486904F00CAA79B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB371486904F00CAA79B /* ViewController.m */; }; 23 | 3780FB3B1486904F00CAA79B /* ViewController_iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3780FB391486904F00CAA79B /* ViewController_iPhone.xib */; }; 24 | 3780FB3E1486904F00CAA79B /* ViewController_iPad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3780FB3C1486904F00CAA79B /* ViewController_iPad.xib */; }; 25 | 3780FB46148693D800CAA79B /* CoreLocation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780FB44148693D800CAA79B /* CoreLocation.framework */; }; 26 | 3780FB47148693D800CAA79B /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780FB45148693D800CAA79B /* MapKit.framework */; }; 27 | 3780FB49148693FA00CAA79B /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3780FB48148693FA00CAA79B /* QuartzCore.framework */; }; 28 | 3780FB57148694A500CAA79B /* GenericPinAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB50148694A500CAA79B /* GenericPinAnnotationView.m */; }; 29 | 3780FB58148694A500CAA79B /* MultiRowCalloutAnnotationView.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB52148694A500CAA79B /* MultiRowCalloutAnnotationView.m */; }; 30 | 3780FB59148694A500CAA79B /* MultiRowCalloutCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB54148694A500CAA79B /* MultiRowCalloutCell.m */; }; 31 | 3780FB5C14869A9800CAA79B /* District.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB5B14869A9800CAA79B /* District.m */; }; 32 | 3780FB5F1486A1A700CAA79B /* Representative.m in Sources */ = {isa = PBXBuildFile; fileRef = 3780FB5E1486A1A700CAA79B /* Representative.m */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 372F011B1A1DA8020075D7A5 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; 37 | 3770F11B1486DD2E00FD854B /* bluestar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bluestar.png; sourceTree = ""; }; 38 | 3770F11C1486DD2E00FD854B /* bluestar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bluestar@2x.png"; sourceTree = ""; }; 39 | 3770F11D1486DD2E00FD854B /* redstar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = redstar.png; sourceTree = ""; }; 40 | 3770F11E1486DD2E00FD854B /* redstar@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "redstar@2x.png"; sourceTree = ""; }; 41 | 3770F13D14875BB600FD854B /* MultiRowAnnotation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiRowAnnotation.h; sourceTree = ""; }; 42 | 3770F13E14875BB600FD854B /* MultiRowAnnotation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiRowAnnotation.m; sourceTree = ""; }; 43 | 3770F13F14875BB600FD854B /* MultiRowAnnotationProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiRowAnnotationProtocol.h; sourceTree = ""; }; 44 | 3780FB201486904F00CAA79B /* CalloutDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CalloutDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 45 | 3780FB241486904F00CAA79B /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 46 | 3780FB261486904F00CAA79B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 47 | 3780FB281486904F00CAA79B /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 48 | 3780FB2C1486904F00CAA79B /* CalloutDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CalloutDemo-Info.plist"; sourceTree = ""; }; 49 | 3780FB2E1486904F00CAA79B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 50 | 3780FB301486904F00CAA79B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 51 | 3780FB321486904F00CAA79B /* CalloutDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CalloutDemo-Prefix.pch"; sourceTree = ""; }; 52 | 3780FB331486904F00CAA79B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 53 | 3780FB341486904F00CAA79B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 54 | 3780FB361486904F00CAA79B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 55 | 3780FB371486904F00CAA79B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 56 | 3780FB3A1486904F00CAA79B /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPhone.xib; sourceTree = ""; }; 57 | 3780FB3D1486904F00CAA79B /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/ViewController_iPad.xib; sourceTree = ""; }; 58 | 3780FB44148693D800CAA79B /* CoreLocation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreLocation.framework; path = System/Library/Frameworks/CoreLocation.framework; sourceTree = SDKROOT; }; 59 | 3780FB45148693D800CAA79B /* MapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MapKit.framework; path = System/Library/Frameworks/MapKit.framework; sourceTree = SDKROOT; }; 60 | 3780FB48148693FA00CAA79B /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 61 | 3780FB4F148694A500CAA79B /* GenericPinAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GenericPinAnnotationView.h; sourceTree = ""; }; 62 | 3780FB50148694A500CAA79B /* GenericPinAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GenericPinAnnotationView.m; sourceTree = ""; }; 63 | 3780FB51148694A500CAA79B /* MultiRowCalloutAnnotationView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiRowCalloutAnnotationView.h; sourceTree = ""; }; 64 | 3780FB52148694A500CAA79B /* MultiRowCalloutAnnotationView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiRowCalloutAnnotationView.m; sourceTree = ""; }; 65 | 3780FB53148694A500CAA79B /* MultiRowCalloutCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MultiRowCalloutCell.h; sourceTree = ""; }; 66 | 3780FB54148694A500CAA79B /* MultiRowCalloutCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MultiRowCalloutCell.m; sourceTree = ""; }; 67 | 3780FB5A14869A9800CAA79B /* District.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = District.h; sourceTree = ""; }; 68 | 3780FB5B14869A9800CAA79B /* District.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = District.m; sourceTree = ""; }; 69 | 3780FB5D1486A1A700CAA79B /* Representative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Representative.h; sourceTree = ""; }; 70 | 3780FB5E1486A1A700CAA79B /* Representative.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Representative.m; sourceTree = ""; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 3780FB1D1486904F00CAA79B /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | 3780FB49148693FA00CAA79B /* QuartzCore.framework in Frameworks */, 79 | 3780FB46148693D800CAA79B /* CoreLocation.framework in Frameworks */, 80 | 3780FB47148693D800CAA79B /* MapKit.framework in Frameworks */, 81 | 3780FB251486904F00CAA79B /* UIKit.framework in Frameworks */, 82 | 3780FB271486904F00CAA79B /* Foundation.framework in Frameworks */, 83 | 3780FB291486904F00CAA79B /* CoreGraphics.framework in Frameworks */, 84 | ); 85 | runOnlyForDeploymentPostprocessing = 0; 86 | }; 87 | /* End PBXFrameworksBuildPhase section */ 88 | 89 | /* Begin PBXGroup section */ 90 | 3770F11A1486DD2E00FD854B /* Assets */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 3770F11B1486DD2E00FD854B /* bluestar.png */, 94 | 3770F11C1486DD2E00FD854B /* bluestar@2x.png */, 95 | 3770F11D1486DD2E00FD854B /* redstar.png */, 96 | 3770F11E1486DD2E00FD854B /* redstar@2x.png */, 97 | ); 98 | path = Assets; 99 | sourceTree = ""; 100 | }; 101 | 3780FB151486904F00CAA79B = { 102 | isa = PBXGroup; 103 | children = ( 104 | 372F011B1A1DA8020075D7A5 /* Default-568h@2x.png */, 105 | 3780FB4A148694A500CAA79B /* MultiRowCalloutAnnotationView */, 106 | 3780FB2A1486904F00CAA79B /* CalloutDemo */, 107 | 3780FB231486904F00CAA79B /* Frameworks */, 108 | 3780FB211486904F00CAA79B /* Products */, 109 | ); 110 | sourceTree = ""; 111 | }; 112 | 3780FB211486904F00CAA79B /* Products */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 3780FB201486904F00CAA79B /* CalloutDemo.app */, 116 | ); 117 | name = Products; 118 | sourceTree = ""; 119 | }; 120 | 3780FB231486904F00CAA79B /* Frameworks */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 3780FB48148693FA00CAA79B /* QuartzCore.framework */, 124 | 3780FB44148693D800CAA79B /* CoreLocation.framework */, 125 | 3780FB45148693D800CAA79B /* MapKit.framework */, 126 | 3780FB241486904F00CAA79B /* UIKit.framework */, 127 | 3780FB261486904F00CAA79B /* Foundation.framework */, 128 | 3780FB281486904F00CAA79B /* CoreGraphics.framework */, 129 | ); 130 | name = Frameworks; 131 | sourceTree = ""; 132 | }; 133 | 3780FB2A1486904F00CAA79B /* CalloutDemo */ = { 134 | isa = PBXGroup; 135 | children = ( 136 | 3780FB331486904F00CAA79B /* AppDelegate.h */, 137 | 3780FB341486904F00CAA79B /* AppDelegate.m */, 138 | 3780FB361486904F00CAA79B /* ViewController.h */, 139 | 3780FB371486904F00CAA79B /* ViewController.m */, 140 | 3780FB5A14869A9800CAA79B /* District.h */, 141 | 3780FB5B14869A9800CAA79B /* District.m */, 142 | 3780FB5D1486A1A700CAA79B /* Representative.h */, 143 | 3780FB5E1486A1A700CAA79B /* Representative.m */, 144 | 3780FB391486904F00CAA79B /* ViewController_iPhone.xib */, 145 | 3780FB3C1486904F00CAA79B /* ViewController_iPad.xib */, 146 | 3770F11A1486DD2E00FD854B /* Assets */, 147 | 3780FB2B1486904F00CAA79B /* Supporting Files */, 148 | ); 149 | path = CalloutDemo; 150 | sourceTree = ""; 151 | }; 152 | 3780FB2B1486904F00CAA79B /* Supporting Files */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 3780FB2C1486904F00CAA79B /* CalloutDemo-Info.plist */, 156 | 3780FB2D1486904F00CAA79B /* InfoPlist.strings */, 157 | 3780FB301486904F00CAA79B /* main.m */, 158 | 3780FB321486904F00CAA79B /* CalloutDemo-Prefix.pch */, 159 | ); 160 | name = "Supporting Files"; 161 | sourceTree = ""; 162 | }; 163 | 3780FB4A148694A500CAA79B /* MultiRowCalloutAnnotationView */ = { 164 | isa = PBXGroup; 165 | children = ( 166 | 3780FB4F148694A500CAA79B /* GenericPinAnnotationView.h */, 167 | 3780FB50148694A500CAA79B /* GenericPinAnnotationView.m */, 168 | 3780FB51148694A500CAA79B /* MultiRowCalloutAnnotationView.h */, 169 | 3780FB52148694A500CAA79B /* MultiRowCalloutAnnotationView.m */, 170 | 3780FB53148694A500CAA79B /* MultiRowCalloutCell.h */, 171 | 3780FB54148694A500CAA79B /* MultiRowCalloutCell.m */, 172 | 3770F13D14875BB600FD854B /* MultiRowAnnotation.h */, 173 | 3770F13E14875BB600FD854B /* MultiRowAnnotation.m */, 174 | 3770F13F14875BB600FD854B /* MultiRowAnnotationProtocol.h */, 175 | ); 176 | name = MultiRowCalloutAnnotationView; 177 | path = ../MultiRowCalloutAnnotationView; 178 | sourceTree = ""; 179 | }; 180 | /* End PBXGroup section */ 181 | 182 | /* Begin PBXNativeTarget section */ 183 | 3780FB1F1486904F00CAA79B /* CalloutDemo */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 3780FB411486904F00CAA79B /* Build configuration list for PBXNativeTarget "CalloutDemo" */; 186 | buildPhases = ( 187 | 3780FB1C1486904F00CAA79B /* Sources */, 188 | 3780FB1D1486904F00CAA79B /* Frameworks */, 189 | 3780FB1E1486904F00CAA79B /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | ); 195 | name = CalloutDemo; 196 | productName = CalloutDemo; 197 | productReference = 3780FB201486904F00CAA79B /* CalloutDemo.app */; 198 | productType = "com.apple.product-type.application"; 199 | }; 200 | /* End PBXNativeTarget section */ 201 | 202 | /* Begin PBXProject section */ 203 | 3780FB171486904F00CAA79B /* Project object */ = { 204 | isa = PBXProject; 205 | attributes = { 206 | LastUpgradeCheck = 0610; 207 | }; 208 | buildConfigurationList = 3780FB1A1486904F00CAA79B /* Build configuration list for PBXProject "CalloutDemo" */; 209 | compatibilityVersion = "Xcode 3.2"; 210 | developmentRegion = English; 211 | hasScannedForEncodings = 0; 212 | knownRegions = ( 213 | en, 214 | ); 215 | mainGroup = 3780FB151486904F00CAA79B; 216 | productRefGroup = 3780FB211486904F00CAA79B /* Products */; 217 | projectDirPath = ""; 218 | projectRoot = ""; 219 | targets = ( 220 | 3780FB1F1486904F00CAA79B /* CalloutDemo */, 221 | ); 222 | }; 223 | /* End PBXProject section */ 224 | 225 | /* Begin PBXResourcesBuildPhase section */ 226 | 3780FB1E1486904F00CAA79B /* Resources */ = { 227 | isa = PBXResourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 3780FB2F1486904F00CAA79B /* InfoPlist.strings in Resources */, 231 | 3780FB3B1486904F00CAA79B /* ViewController_iPhone.xib in Resources */, 232 | 3780FB3E1486904F00CAA79B /* ViewController_iPad.xib in Resources */, 233 | 3770F11F1486DD2E00FD854B /* bluestar.png in Resources */, 234 | 3770F1201486DD2E00FD854B /* bluestar@2x.png in Resources */, 235 | 372F011C1A1DA8020075D7A5 /* Default-568h@2x.png in Resources */, 236 | 3770F1211486DD2E00FD854B /* redstar.png in Resources */, 237 | 3770F1221486DD2E00FD854B /* redstar@2x.png in Resources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | /* End PBXResourcesBuildPhase section */ 242 | 243 | /* Begin PBXSourcesBuildPhase section */ 244 | 3780FB1C1486904F00CAA79B /* Sources */ = { 245 | isa = PBXSourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 3780FB311486904F00CAA79B /* main.m in Sources */, 249 | 3780FB351486904F00CAA79B /* AppDelegate.m in Sources */, 250 | 3780FB381486904F00CAA79B /* ViewController.m in Sources */, 251 | 3780FB57148694A500CAA79B /* GenericPinAnnotationView.m in Sources */, 252 | 3780FB58148694A500CAA79B /* MultiRowCalloutAnnotationView.m in Sources */, 253 | 3780FB59148694A500CAA79B /* MultiRowCalloutCell.m in Sources */, 254 | 3780FB5C14869A9800CAA79B /* District.m in Sources */, 255 | 3780FB5F1486A1A700CAA79B /* Representative.m in Sources */, 256 | 3770F14014875BB600FD854B /* MultiRowAnnotation.m in Sources */, 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXSourcesBuildPhase section */ 261 | 262 | /* Begin PBXVariantGroup section */ 263 | 3780FB2D1486904F00CAA79B /* InfoPlist.strings */ = { 264 | isa = PBXVariantGroup; 265 | children = ( 266 | 3780FB2E1486904F00CAA79B /* en */, 267 | ); 268 | name = InfoPlist.strings; 269 | sourceTree = ""; 270 | }; 271 | 3780FB391486904F00CAA79B /* ViewController_iPhone.xib */ = { 272 | isa = PBXVariantGroup; 273 | children = ( 274 | 3780FB3A1486904F00CAA79B /* en */, 275 | ); 276 | name = ViewController_iPhone.xib; 277 | sourceTree = ""; 278 | }; 279 | 3780FB3C1486904F00CAA79B /* ViewController_iPad.xib */ = { 280 | isa = PBXVariantGroup; 281 | children = ( 282 | 3780FB3D1486904F00CAA79B /* en */, 283 | ); 284 | name = ViewController_iPad.xib; 285 | sourceTree = ""; 286 | }; 287 | /* End PBXVariantGroup section */ 288 | 289 | /* Begin XCBuildConfiguration section */ 290 | 3780FB3F1486904F00CAA79B /* Debug */ = { 291 | isa = XCBuildConfiguration; 292 | buildSettings = { 293 | ALWAYS_SEARCH_USER_PATHS = NO; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_CONSTANT_CONVERSION = YES; 296 | CLANG_WARN_EMPTY_BODY = YES; 297 | CLANG_WARN_ENUM_CONVERSION = YES; 298 | CLANG_WARN_INT_CONVERSION = YES; 299 | CLANG_WARN_UNREACHABLE_CODE = YES; 300 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 301 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 302 | COPY_PHASE_STRIP = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 305 | GCC_DYNAMIC_NO_PIC = NO; 306 | GCC_OPTIMIZATION_LEVEL = 0; 307 | GCC_PREPROCESSOR_DEFINITIONS = ( 308 | "DEBUG=1", 309 | "$(inherited)", 310 | ); 311 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 312 | GCC_THUMB_SUPPORT = NO; 313 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 314 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 315 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 316 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 317 | GCC_WARN_UNDECLARED_SELECTOR = YES; 318 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 319 | GCC_WARN_UNUSED_FUNCTION = YES; 320 | GCC_WARN_UNUSED_VARIABLE = YES; 321 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 322 | ONLY_ACTIVE_ARCH = YES; 323 | SDKROOT = iphoneos; 324 | TARGETED_DEVICE_FAMILY = "1,2"; 325 | }; 326 | name = Debug; 327 | }; 328 | 3780FB401486904F00CAA79B /* Release */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ALWAYS_SEARCH_USER_PATHS = NO; 332 | CLANG_WARN_BOOL_CONVERSION = YES; 333 | CLANG_WARN_CONSTANT_CONVERSION = YES; 334 | CLANG_WARN_EMPTY_BODY = YES; 335 | CLANG_WARN_ENUM_CONVERSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 340 | COPY_PHASE_STRIP = YES; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = "compiler-default"; 343 | GCC_THUMB_SUPPORT = NO; 344 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 345 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 346 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 347 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 348 | GCC_WARN_UNDECLARED_SELECTOR = YES; 349 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 350 | GCC_WARN_UNUSED_FUNCTION = YES; 351 | GCC_WARN_UNUSED_VARIABLE = YES; 352 | IPHONEOS_DEPLOYMENT_TARGET = 8.1; 353 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 354 | SDKROOT = iphoneos; 355 | TARGETED_DEVICE_FAMILY = "1,2"; 356 | VALIDATE_PRODUCT = YES; 357 | }; 358 | name = Release; 359 | }; 360 | 3780FB421486904F00CAA79B /* Debug */ = { 361 | isa = XCBuildConfiguration; 362 | buildSettings = { 363 | CLANG_ENABLE_OBJC_ARC = YES; 364 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 365 | GCC_PREFIX_HEADER = "CalloutDemo/CalloutDemo-Prefix.pch"; 366 | GCC_THUMB_SUPPORT = NO; 367 | INFOPLIST_FILE = "CalloutDemo/CalloutDemo-Info.plist"; 368 | PRODUCT_NAME = "$(TARGET_NAME)"; 369 | TARGETED_DEVICE_FAMILY = "1,2"; 370 | WRAPPER_EXTENSION = app; 371 | }; 372 | name = Debug; 373 | }; 374 | 3780FB431486904F00CAA79B /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | CLANG_ENABLE_OBJC_ARC = YES; 378 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 379 | GCC_PREFIX_HEADER = "CalloutDemo/CalloutDemo-Prefix.pch"; 380 | GCC_THUMB_SUPPORT = NO; 381 | INFOPLIST_FILE = "CalloutDemo/CalloutDemo-Info.plist"; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | TARGETED_DEVICE_FAMILY = "1,2"; 384 | WRAPPER_EXTENSION = app; 385 | }; 386 | name = Release; 387 | }; 388 | /* End XCBuildConfiguration section */ 389 | 390 | /* Begin XCConfigurationList section */ 391 | 3780FB1A1486904F00CAA79B /* Build configuration list for PBXProject "CalloutDemo" */ = { 392 | isa = XCConfigurationList; 393 | buildConfigurations = ( 394 | 3780FB3F1486904F00CAA79B /* Debug */, 395 | 3780FB401486904F00CAA79B /* Release */, 396 | ); 397 | defaultConfigurationIsVisible = 0; 398 | defaultConfigurationName = Release; 399 | }; 400 | 3780FB411486904F00CAA79B /* Build configuration list for PBXNativeTarget "CalloutDemo" */ = { 401 | isa = XCConfigurationList; 402 | buildConfigurations = ( 403 | 3780FB421486904F00CAA79B /* Debug */, 404 | 3780FB431486904F00CAA79B /* Release */, 405 | ); 406 | defaultConfigurationIsVisible = 0; 407 | defaultConfigurationName = Release; 408 | }; 409 | /* End XCConfigurationList section */ 410 | }; 411 | rootObject = 3780FB171486904F00CAA79B /* Project object */; 412 | } 413 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | 15 | @class ViewController; 16 | 17 | @interface AppDelegate : UIResponder 18 | @property (nonatomic,strong) UIWindow *window; 19 | @property (nonatomic,strong) ViewController *viewController; 20 | @end 21 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "AppDelegate.h" 14 | #import "ViewController.h" 15 | 16 | @implementation AppDelegate 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 19 | { 20 | _window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 21 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) 22 | { 23 | _viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPhone" bundle:nil]; 24 | } 25 | else 26 | { 27 | _viewController = [[ViewController alloc] initWithNibName:@"ViewController_iPad" bundle:nil]; 28 | } 29 | _window.rootViewController = _viewController; 30 | [_window makeKeyAndVisible]; 31 | 32 | return YES; 33 | } 34 | 35 | - (void)applicationWillResignActive:(UIApplication *)application 36 | { 37 | } 38 | 39 | - (void)applicationDidEnterBackground:(UIApplication *)application 40 | { 41 | } 42 | 43 | - (void)applicationWillEnterForeground:(UIApplication *)application 44 | { 45 | } 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application 48 | { 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application 52 | { 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/Assets/bluestar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grgcombs/MultiRowCalloutAnnotationView/8c043825f3c034131ff0f2e44b1190d25d07d137/CalloutDemo/CalloutDemo/Assets/bluestar.png -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/Assets/bluestar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grgcombs/MultiRowCalloutAnnotationView/8c043825f3c034131ff0f2e44b1190d25d07d137/CalloutDemo/CalloutDemo/Assets/bluestar@2x.png -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/Assets/redstar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grgcombs/MultiRowCalloutAnnotationView/8c043825f3c034131ff0f2e44b1190d25d07d137/CalloutDemo/CalloutDemo/Assets/redstar.png -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/Assets/redstar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grgcombs/MultiRowCalloutAnnotationView/8c043825f3c034131ff0f2e44b1190d25d07d137/CalloutDemo/CalloutDemo/Assets/redstar@2x.png -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/CalloutDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFiles 12 | 13 | CFBundleIdentifier 14 | com.mycompany.${PRODUCT_NAME:rfc1034identifier} 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleVersion 26 | 1.0 27 | LSRequiresIPhoneOS 28 | 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | UIInterfaceOrientationPortraitUpsideDown 39 | 40 | UISupportedInterfaceOrientations~ipad 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationPortraitUpsideDown 44 | UIInterfaceOrientationLandscapeLeft 45 | UIInterfaceOrientationLandscapeRight 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/CalloutDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CalloutDemo' target in the 'CalloutDemo' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_4_0 8 | #warning "This project uses features only available in iOS SDK 4.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/District.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoMapAnnotation.h 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | #import "MultiRowAnnotationProtocol.h" 15 | 16 | @interface District : NSObject 17 | @property (nonatomic,copy) NSString *title; 18 | @property (nonatomic,assign) CLLocationCoordinate2D coordinate; 19 | @property (unsafe_unretained, nonatomic,readonly) NSArray *calloutCells; // MultiRowCalloutCells of representatives 20 | @property (nonatomic,strong) NSArray *representatives; 21 | + (instancetype)districtWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title representatives:(NSArray *)representatives;; 22 | + (instancetype)demoAnnotationFactory; 23 | @end 24 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/District.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoMapAnnotation.m 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "District.h" 14 | #import "Representative.h" 15 | 16 | @implementation District 17 | 18 | #pragma mark For Demonstration Purposes 19 | 20 | /* Naturally, you should set up your annotation objects as usual, but this demo factory helps distance the cell data from the view controller. */ 21 | + (instancetype)demoAnnotationFactory 22 | { 23 | Representative *dudeOne = [Representative representativeWithName:@"Rep. Dude" party:@"Republican" image:[UIImage imageNamed:@"redstar"] representativeID:@"TXL1"]; 24 | Representative *dudeTwo = [Representative representativeWithName:@"Rep. Guy" party:@"Democrat" image:[UIImage imageNamed:@"bluestar"] representativeID:@"TXL2"]; 25 | return [District districtWithCoordinate:CLLocationCoordinate2DMake(30.274722, -97.740556) title:@"Austin Representatives" representatives:@[dudeOne, dudeTwo]]; 26 | } 27 | 28 | #pragma mark - The Good Stuff 29 | 30 | + (instancetype)districtWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title representatives:(NSArray *)representatives 31 | { 32 | return [[District alloc] initWithCoordinate:coordinate title:title representatives:representatives]; 33 | } 34 | 35 | - (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title representatives:(NSArray *)representatives 36 | { 37 | self = [super init]; 38 | if (self) 39 | { 40 | _coordinate = coordinate; 41 | _title = title; 42 | _representatives = representatives; 43 | } 44 | return self; 45 | } 46 | 47 | 48 | - (NSArray *)calloutCells 49 | { 50 | if (!_representatives || [_representatives count] == 0) 51 | return nil; 52 | return [self valueForKeyPath:@"representatives.calloutCell"]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/Representative.h: -------------------------------------------------------------------------------- 1 | // 2 | // Representative.h 3 | // Created by Greg Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | 15 | @class MultiRowCalloutCell; 16 | 17 | @interface Representative : NSObject 18 | @property (nonatomic,copy) NSString *name; 19 | @property (nonatomic,copy) NSString *party; 20 | @property (nonatomic,strong) UIImage *image; 21 | @property (nonatomic,copy) NSString *representativeID; 22 | @property (unsafe_unretained, nonatomic,readonly) MultiRowCalloutCell *calloutCell; 23 | 24 | + (instancetype)representativeWithName:(NSString *)name party:(NSString *)party image:(UIImage *)image representativeID:(NSString *)representativeID; 25 | @end 26 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/Representative.m: -------------------------------------------------------------------------------- 1 | // 2 | // Representative.m 3 | // Created by Greg Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "Representative.h" 14 | #import "MultiRowCalloutCell.h" 15 | 16 | @implementation Representative 17 | 18 | + (instancetype)representativeWithName:(NSString *)name party:(NSString *)party image:(UIImage *)image representativeID:(NSString *)representativeID 19 | { 20 | return [[Representative alloc] initWithName:name party:party image:image representativeID:representativeID]; 21 | } 22 | 23 | - (instancetype)initWithName:(NSString *)name party:(NSString *)party image:(UIImage *)image representativeID:(NSString *)representativeID 24 | { 25 | self = [super init]; 26 | if (self) 27 | { 28 | _name = name; 29 | _party = party; 30 | _image = image; 31 | _representativeID = representativeID; 32 | } 33 | return self; 34 | } 35 | 36 | 37 | - (MultiRowCalloutCell *)calloutCell 38 | { 39 | NSDictionary *dict = nil; 40 | if (_representativeID) 41 | dict = @{@"id": _representativeID}; 42 | return [MultiRowCalloutCell cellWithImage:_image 43 | title:_party 44 | subtitle:_name 45 | userData:dict]; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | #import 15 | 16 | @interface ViewController : UIViewController 17 | @property (nonatomic,strong) IBOutlet MKMapView *mapView; 18 | - (IBAction)changeMapType:(id)sender; 19 | @end 20 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "ViewController.h" 14 | #import "District.h" 15 | #import "GenericPinAnnotationView.h" 16 | #import "MultiRowCalloutAnnotationView.h" 17 | #import "MultiRowAnnotation.h" 18 | 19 | @interface ViewController() 20 | @property (nonatomic,strong) MKAnnotationView *selectedAnnotationView; 21 | @property (nonatomic,strong) MultiRowAnnotation *calloutAnnotation; 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad 27 | { 28 | [super viewDidLoad]; 29 | [_mapView addAnnotation:[District demoAnnotationFactory]]; 30 | } 31 | 32 | #pragma mark - The Good Stuff 33 | 34 | - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation 35 | { 36 | if ([annotation isKindOfClass:[MKUserLocation class]]) 37 | return nil; 38 | 39 | if (![annotation conformsToProtocol:@protocol(MultiRowAnnotationProtocol)]) 40 | return nil; 41 | 42 | NSObject *newAnnotation = (NSObject *)annotation; 43 | if (newAnnotation == _calloutAnnotation) 44 | { 45 | MultiRowCalloutAnnotationView *annotationView = (MultiRowCalloutAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:MultiRowCalloutReuseIdentifier]; 46 | if (!annotationView) 47 | { 48 | annotationView = [MultiRowCalloutAnnotationView calloutWithAnnotation:newAnnotation onCalloutAccessoryTapped:^(MultiRowCalloutCell *cell, UIControl *control, NSDictionary *userData) { 49 | // This is where I usually push in a new detail view onto the navigation controller stack, using the object's ID 50 | NSLog(@"Representative (%@) with ID '%@' was tapped.", cell.subtitle, userData[@"id"]); 51 | }]; 52 | } 53 | else 54 | { 55 | annotationView.annotation = newAnnotation; 56 | } 57 | annotationView.parentAnnotationView = _selectedAnnotationView; 58 | annotationView.mapView = mapView; 59 | return annotationView; 60 | } 61 | GenericPinAnnotationView *annotationView = (GenericPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:GenericPinReuseIdentifier]; 62 | if (!annotationView) 63 | { 64 | annotationView = [GenericPinAnnotationView pinViewWithAnnotation:newAnnotation]; 65 | } 66 | annotationView.annotation = newAnnotation; 67 | return annotationView; 68 | } 69 | 70 | - (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)aView 71 | { 72 | id annotation = aView.annotation; 73 | if (!annotation || ![aView isSelected]) 74 | return; 75 | if ( NO == [annotation isKindOfClass:[MultiRowCalloutCell class]] && 76 | [annotation conformsToProtocol:@protocol(MultiRowAnnotationProtocol)] ) 77 | { 78 | NSObject *pinAnnotation = (NSObject *)annotation; 79 | if (!_calloutAnnotation) 80 | { 81 | _calloutAnnotation = [[MultiRowAnnotation alloc] init]; 82 | [_calloutAnnotation copyAttributesFromAnnotation:pinAnnotation]; 83 | [mapView addAnnotation:_calloutAnnotation]; 84 | } 85 | _selectedAnnotationView = aView; 86 | return; 87 | } 88 | [mapView setCenterCoordinate:annotation.coordinate animated:YES]; 89 | _selectedAnnotationView = aView; 90 | } 91 | 92 | - (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)aView 93 | { 94 | if ( NO == [aView.annotation conformsToProtocol:@protocol(MultiRowAnnotationProtocol)] ) 95 | return; 96 | if ([aView.annotation isKindOfClass:[MultiRowAnnotation class]]) 97 | return; 98 | GenericPinAnnotationView *pinView = (GenericPinAnnotationView *)aView; 99 | if (_calloutAnnotation && !pinView.preventSelectionChange) 100 | { 101 | [mapView removeAnnotation:_calloutAnnotation]; 102 | _calloutAnnotation = nil; 103 | } 104 | } 105 | 106 | #pragma mark - Boilerplate Stuff 107 | 108 | - (IBAction)changeMapType:(id)sender 109 | { 110 | if (sender && [sender respondsToSelector:@selector(selectedSegmentIndex)]) 111 | { 112 | NSInteger index = [sender selectedSegmentIndex]; 113 | _mapView.mapType = index; 114 | } 115 | } 116 | 117 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 118 | { 119 | return YES; 120 | } 121 | 122 | - (void)didReceiveMemoryWarning 123 | { 124 | [super didReceiveMemoryWarning]; 125 | if (self.isViewLoaded && _mapView && _mapView.annotations) 126 | { 127 | [_mapView removeAnnotations:_mapView.annotations]; 128 | } 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/en.lproj/ViewController_iPad.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11C74 6 | 1938 7 | 1138.23 8 | 567.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | IBUIBarButtonItem 15 | IBUIToolbar 16 | IBUISegmentedControl 17 | IBUIView 18 | IBMKMapView 19 | IBProxyObject 20 | 21 | 22 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 23 | 24 | 25 | PluginDependencyRecalculationVersion 26 | 27 | 28 | 29 | 30 | IBFilesOwner 31 | IBIPadFramework 32 | 33 | 34 | IBFirstResponder 35 | IBIPadFramework 36 | 37 | 38 | 39 | 274 40 | 41 | 42 | 43 | 266 44 | 45 | 46 | 47 | 288 48 | {{280, 8}, {207, 30}} 49 | 50 | 51 | 52 | _NS:289 53 | 54 | 1 55 | MC40NTQ5MDE5NjM1IDAuNjgyMzUyOTYwMSAwLjY0NzA1ODg0NDYAA 56 | 57 | NO 58 | IBIPadFramework 59 | 2 60 | 3 61 | 0 62 | 63 | Map 64 | Satellite 65 | Hybrid 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {0, 0} 79 | {0, 0} 80 | {0, 0} 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 1 89 | MC40NTQ5MDE5NjM1IDAuNDU4ODIzNTMxOSAwLjQxOTYwNzg0NzkAA 90 | 91 | 92 | 93 | {{0, 960}, {768, 44}} 94 | 95 | 96 | 97 | _NS:372 98 | 99 | 3 100 | MAA 101 | 102 | NO 103 | IBIPadFramework 104 | 105 | 106 | IBIPadFramework 107 | 1 108 | 109 | 5 110 | 111 | 112 | IBIPadFramework 113 | 114 | 115 | 116 | 1 117 | MC40NTQ5MDE5NjM1IDAuNDU4ODIzNTMxOSAwLjQxOTYwNzg0NzkAA 118 | 119 | 120 | 121 | IBIPadFramework 122 | 1 123 | 124 | 5 125 | 126 | 127 | 128 | 1 129 | MC40NTQ5MDE5NjM1IDAuNjgyMzUyOTYwMSAwLjY0NzA1ODg0NDYAA 130 | 131 | 132 | 133 | 134 | 274 135 | {768, 960} 136 | 137 | 138 | 139 | _NS:801 140 | 141 | YES 142 | YES 143 | IBIPadFramework 144 | 145 | 146 | {{0, 20}, {768, 1004}} 147 | 148 | 149 | 150 | 151 | 3 152 | MQA 153 | 154 | 2 155 | 156 | 157 | 158 | 2 159 | 160 | IBIPadFramework 161 | 162 | 163 | 164 | 165 | 166 | 167 | view 168 | 169 | 170 | 171 | 3 172 | 173 | 174 | 175 | mapView 176 | 177 | 178 | 179 | 6 180 | 181 | 182 | 183 | delegate 184 | 185 | 186 | 187 | 5 188 | 189 | 190 | 191 | changeMapType: 192 | 193 | 194 | 13 195 | 196 | 12 197 | 198 | 199 | 200 | 201 | 202 | 0 203 | 204 | 205 | 206 | 207 | 208 | -1 209 | 210 | 211 | File's Owner 212 | 213 | 214 | -2 215 | 216 | 217 | 218 | 219 | 2 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 4 229 | 230 | 231 | 232 | 233 | 7 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 8 244 | 245 | 246 | 247 | 248 | 10 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 9 257 | 258 | 259 | 260 | 261 | 11 262 | 263 | 264 | 265 | 266 | 267 | 268 | ViewController 269 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 270 | UIResponder 271 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 272 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 273 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 274 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 275 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 276 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 277 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 278 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 279 | 280 | 281 | 282 | 283 | 284 | 285 | 12 286 | 287 | 288 | 289 | 290 | ViewController 291 | UIViewController 292 | 293 | changeMapType: 294 | id 295 | 296 | 297 | changeMapType: 298 | 299 | changeMapType: 300 | id 301 | 302 | 303 | 304 | mapView 305 | MKMapView 306 | 307 | 308 | mapView 309 | 310 | mapView 311 | MKMapView 312 | 313 | 314 | 315 | IBProjectSource 316 | ./Classes/ViewController.h 317 | 318 | 319 | 320 | 321 | 0 322 | IBIPadFramework 323 | YES 324 | 3 325 | 933 326 | 327 | 328 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/en.lproj/ViewController_iPhone.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1280 5 | 11C74 6 | 1938 7 | 1138.23 8 | 567.00 9 | 10 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 11 | 933 12 | 13 | 14 | IBUIBarButtonItem 15 | IBUIToolbar 16 | IBUISegmentedControl 17 | IBUIView 18 | IBMKMapView 19 | IBProxyObject 20 | 21 | 22 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 23 | 24 | 25 | PluginDependencyRecalculationVersion 26 | 27 | 28 | 29 | 30 | IBFilesOwner 31 | IBCocoaTouchFramework 32 | 33 | 34 | IBFirstResponder 35 | IBCocoaTouchFramework 36 | 37 | 38 | 39 | 274 40 | 41 | 42 | 43 | 266 44 | 45 | 46 | 47 | 288 48 | {{56, 8}, {207, 30}} 49 | 50 | 51 | 52 | _NS:289 53 | 54 | 1 55 | MC40NTQ5MDE5NjM1IDAuNjgyMzUyOTYwMSAwLjY0NzA1ODg0NDYAA 56 | 57 | NO 58 | IBCocoaTouchFramework 59 | 2 60 | 3 61 | 0 62 | 63 | Map 64 | Satellite 65 | Hybrid 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | {0, 0} 79 | {0, 0} 80 | {0, 0} 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 1 89 | MC40NTQ5MDE5NjM1IDAuNDU4ODIzNTMxOSAwLjQxOTYwNzg0NzkAA 90 | 91 | 92 | 93 | {{0, 416}, {320, 44}} 94 | 95 | 96 | 97 | _NS:372 98 | 99 | 3 100 | MAA 101 | 102 | NO 103 | IBCocoaTouchFramework 104 | 105 | 106 | IBCocoaTouchFramework 107 | 1 108 | 109 | 5 110 | 111 | 112 | IBCocoaTouchFramework 113 | 114 | 115 | 116 | 1 117 | MC40NTQ5MDE5NjM1IDAuNDU4ODIzNTMxOSAwLjQxOTYwNzg0NzkAA 118 | 119 | 120 | 121 | IBCocoaTouchFramework 122 | 1 123 | 124 | 5 125 | 126 | 127 | 128 | 1 129 | MC40NTQ5MDE5NjM1IDAuNjgyMzUyOTYwMSAwLjY0NzA1ODg0NDYAA 130 | 131 | 132 | 133 | 134 | 274 135 | {320, 416} 136 | 137 | 138 | 139 | _NS:800 140 | YES 141 | YES 142 | IBCocoaTouchFramework 143 | 144 | 145 | {{0, 20}, {320, 460}} 146 | 147 | 148 | 149 | 150 | 3 151 | MC43NQA 152 | 153 | 2 154 | 155 | 156 | NO 157 | 158 | IBCocoaTouchFramework 159 | 160 | 161 | 162 | 163 | 164 | 165 | view 166 | 167 | 168 | 169 | 7 170 | 171 | 172 | 173 | mapView 174 | 175 | 176 | 177 | 10 178 | 179 | 180 | 181 | delegate 182 | 183 | 184 | 185 | 9 186 | 187 | 188 | 189 | changeMapType: 190 | 191 | 192 | 13 193 | 194 | 16 195 | 196 | 197 | 198 | 199 | 200 | 0 201 | 202 | 203 | 204 | 205 | 206 | -1 207 | 208 | 209 | File's Owner 210 | 211 | 212 | -2 213 | 214 | 215 | 216 | 217 | 6 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 8 227 | 228 | 229 | 230 | 231 | 11 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 12 242 | 243 | 244 | 245 | 246 | 13 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 14 255 | 256 | 257 | 258 | 259 | 15 260 | 261 | 262 | 263 | 264 | 265 | 266 | ViewController 267 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 268 | UIResponder 269 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 270 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 271 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 272 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 273 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 274 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 275 | 276 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 277 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 278 | 279 | 280 | 281 | 282 | 283 | 16 284 | 285 | 286 | 287 | 288 | ViewController 289 | UIViewController 290 | 291 | changeMapType: 292 | id 293 | 294 | 295 | changeMapType: 296 | 297 | changeMapType: 298 | id 299 | 300 | 301 | 302 | mapView 303 | MKMapView 304 | 305 | 306 | mapView 307 | 308 | mapView 309 | MKMapView 310 | 311 | 312 | 313 | IBProjectSource 314 | ./Classes/ViewController.h 315 | 316 | 317 | 318 | 319 | 0 320 | IBCocoaTouchFramework 321 | YES 322 | 3 323 | 933 324 | 325 | 326 | -------------------------------------------------------------------------------- /CalloutDemo/CalloutDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | #import "AppDelegate.h" 15 | 16 | int main(int argc, char *argv[]) 17 | { 18 | @autoreleasepool { 19 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CalloutDemo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grgcombs/MultiRowCalloutAnnotationView/8c043825f3c034131ff0f2e44b1190d25d07d137/CalloutDemo/Default-568h@2x.png -------------------------------------------------------------------------------- /LICENSE.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 8 | 9 | Creative Commons Legal Code 10 | 12 | 14 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 27 | 28 |
29 |
30 | 35 | 36 |

Creative Commons Legal Code

37 | 38 |
39 |

Attribution 3.0 Unported

40 |
41 |
42 | 43 |
44 |
45 | 48 | 49 |
50 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES 51 | NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE 52 | DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE 53 | COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. 54 | CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE 55 | INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES 56 | RESULTING FROM ITS USE. 57 |
58 | 59 |

License

60 | 61 |

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS 62 | OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR 63 | "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER 64 | APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS 65 | AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS 66 | PROHIBITED.

67 | 68 |

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU 69 | ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. 70 | TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A 71 | CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE 72 | IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND 73 | CONDITIONS.

74 | 75 |

1. Definitions

76 | 77 |
    78 |
  1. "Adaptation" means a work based upon 79 | the Work, or upon the Work and other pre-existing works, 80 | such as a translation, adaptation, derivative work, 81 | arrangement of music or other alterations of a literary 82 | or artistic work, or phonogram or performance and 83 | includes cinematographic adaptations or any other form in 84 | which the Work may be recast, transformed, or adapted 85 | including in any form recognizably derived from the 86 | original, except that a work that constitutes a 87 | Collection will not be considered an Adaptation for the 88 | purpose of this License. For the avoidance of doubt, 89 | where the Work is a musical work, performance or 90 | phonogram, the synchronization of the Work in 91 | timed-relation with a moving image ("synching") will be 92 | considered an Adaptation for the purpose of this 93 | License.
  2. 94 | 95 |
  3. "Collection" means a collection of 96 | literary or artistic works, such as encyclopedias and 97 | anthologies, or performances, phonograms or broadcasts, 98 | or other works or subject matter other than works listed 99 | in Section 1(f) below, which, by reason of the selection 100 | and arrangement of their contents, constitute 101 | intellectual creations, in which the Work is included in 102 | its entirety in unmodified form along with one or more 103 | other contributions, each constituting separate and 104 | independent works in themselves, which together are 105 | assembled into a collective whole. A work that 106 | constitutes a Collection will not be considered an 107 | Adaptation (as defined above) for the purposes of this 108 | License.
  4. 109 | 110 |
  5. "Distribute" means to make available 111 | to the public the original and copies of the Work or 112 | Adaptation, as appropriate, through sale or other 113 | transfer of ownership.
  6. 114 | 115 |
  7. "Licensor" means the individual, 116 | individuals, entity or entities that offer(s) the Work 117 | under the terms of this License.
  8. 118 | 119 |
  9. "Original Author" means, in the case 120 | of a literary or artistic work, the individual, 121 | individuals, entity or entities who created the Work or 122 | if no individual or entity can be identified, the 123 | publisher; and in addition (i) in the case of a 124 | performance the actors, singers, musicians, dancers, and 125 | other persons who act, sing, deliver, declaim, play in, 126 | interpret or otherwise perform literary or artistic works 127 | or expressions of folklore; (ii) in the case of a 128 | phonogram the producer being the person or legal entity 129 | who first fixes the sounds of a performance or other 130 | sounds; and, (iii) in the case of broadcasts, the 131 | organization that transmits the broadcast.
  10. 132 | 133 |
  11. "Work" means the literary and/or 134 | artistic work offered under the terms of this License 135 | including without limitation any production in the 136 | literary, scientific and artistic domain, whatever may be 137 | the mode or form of its expression including digital 138 | form, such as a book, pamphlet and other writing; a 139 | lecture, address, sermon or other work of the same 140 | nature; a dramatic or dramatico-musical work; a 141 | choreographic work or entertainment in dumb show; a 142 | musical composition with or without words; a 143 | cinematographic work to which are assimilated works 144 | expressed by a process analogous to cinematography; a 145 | work of drawing, painting, architecture, sculpture, 146 | engraving or lithography; a photographic work to which 147 | are assimilated works expressed by a process analogous to 148 | photography; a work of applied art; an illustration, map, 149 | plan, sketch or three-dimensional work relative to 150 | geography, topography, architecture or science; a 151 | performance; a broadcast; a phonogram; a compilation of 152 | data to the extent it is protected as a copyrightable 153 | work; or a work performed by a variety or circus 154 | performer to the extent it is not otherwise considered a 155 | literary or artistic work.
  12. 156 | 157 |
  13. "You" means an individual or entity 158 | exercising rights under this License who has not 159 | previously violated the terms of this License with 160 | respect to the Work, or who has received express 161 | permission from the Licensor to exercise rights under 162 | this License despite a previous violation.
  14. 163 | 164 |
  15. "Publicly Perform" means to perform 165 | public recitations of the Work and to communicate to the 166 | public those public recitations, by any means or process, 167 | including by wire or wireless means or public digital 168 | performances; to make available to the public Works in 169 | such a way that members of the public may access these 170 | Works from a place and at a place individually chosen by 171 | them; to perform the Work to the public by any means or 172 | process and the communication to the public of the 173 | performances of the Work, including by public digital 174 | performance; to broadcast and rebroadcast the Work by any 175 | means including signs, sounds or images.
  16. 176 | 177 |
  17. "Reproduce" means to make copies of 178 | the Work by any means including without limitation by 179 | sound or visual recordings and the right of fixation and 180 | reproducing fixations of the Work, including storage of a 181 | protected performance or phonogram in digital form or 182 | other electronic medium.
  18. 183 |
184 | 185 |

2. Fair Dealing Rights. Nothing in this 186 | License is intended to reduce, limit, or restrict any uses 187 | free from copyright or rights arising from limitations or 188 | exceptions that are provided for in connection with the 189 | copyright protection under copyright law or other 190 | applicable laws.

191 | 192 |

3. License Grant. Subject to the terms 193 | and conditions of this License, Licensor hereby grants You 194 | a worldwide, royalty-free, non-exclusive, perpetual (for 195 | the duration of the applicable copyright) license to 196 | exercise the rights in the Work as stated below:

197 | 198 |
    199 |
  1. to Reproduce the Work, to incorporate the Work into 200 | one or more Collections, and to Reproduce the Work as 201 | incorporated in the Collections;
  2. 202 | 203 |
  3. to create and Reproduce Adaptations provided that any 204 | such Adaptation, including any translation in any medium, 205 | takes reasonable steps to clearly label, demarcate or 206 | otherwise identify that changes were made to the original 207 | Work. For example, a translation could be marked "The 208 | original work was translated from English to Spanish," or 209 | a modification could indicate "The original work has been 210 | modified.";
  4. 211 | 212 |
  5. to Distribute and Publicly Perform the Work including 213 | as incorporated in Collections; and,
  6. 214 | 215 |
  7. to Distribute and Publicly Perform Adaptations.
  8. 216 | 217 |
  9. 218 |

    For the avoidance of doubt:

    219 | 220 |
      221 |
    1. Non-waivable Compulsory License 222 | Schemes. In those jurisdictions in which the 223 | right to collect royalties through any statutory or 224 | compulsory licensing scheme cannot be waived, the 225 | Licensor reserves the exclusive right to collect such 226 | royalties for any exercise by You of the rights 227 | granted under this License;
    2. 228 | 229 |
    3. Waivable Compulsory License 230 | Schemes. In those jurisdictions in which the 231 | right to collect royalties through any statutory or 232 | compulsory licensing scheme can be waived, the 233 | Licensor waives the exclusive right to collect such 234 | royalties for any exercise by You of the rights 235 | granted under this License; and,
    4. 236 | 237 |
    5. Voluntary License Schemes. The 238 | Licensor waives the right to collect royalties, 239 | whether individually or, in the event that the 240 | Licensor is a member of a collecting society that 241 | administers voluntary licensing schemes, via that 242 | society, from any exercise by You of the rights 243 | granted under this License.
    6. 244 |
    245 |
  10. 246 |
247 | 248 |

The above rights may be exercised in all media and 249 | formats whether now known or hereafter devised. The above 250 | rights include the right to make such modifications as are 251 | technically necessary to exercise the rights in other media 252 | and formats. Subject to Section 8(f), all rights not 253 | expressly granted by Licensor are hereby reserved.

254 | 255 |

4. Restrictions. The license granted in 256 | Section 3 above is expressly made subject to and limited by 257 | the following restrictions:

258 | 259 |
    260 |
  1. You may Distribute or Publicly Perform the Work only 261 | under the terms of this License. You must include a copy 262 | of, or the Uniform Resource Identifier (URI) for, this 263 | License with every copy of the Work You Distribute or 264 | Publicly Perform. You may not offer or impose any terms 265 | on the Work that restrict the terms of this License or 266 | the ability of the recipient of the Work to exercise the 267 | rights granted to that recipient under the terms of the 268 | License. You may not sublicense the Work. You must keep 269 | intact all notices that refer to this License and to the 270 | disclaimer of warranties with every copy of the Work You 271 | Distribute or Publicly Perform. When You Distribute or 272 | Publicly Perform the Work, You may not impose any 273 | effective technological measures on the Work that 274 | restrict the ability of a recipient of the Work from You 275 | to exercise the rights granted to that recipient under 276 | the terms of the License. This Section 4(a) applies to 277 | the Work as incorporated in a Collection, but this does 278 | not require the Collection apart from the Work itself to 279 | be made subject to the terms of this License. If You 280 | create a Collection, upon notice from any Licensor You 281 | must, to the extent practicable, remove from the 282 | Collection any credit as required by Section 4(b), as 283 | requested. If You create an Adaptation, upon notice from 284 | any Licensor You must, to the extent practicable, remove 285 | from the Adaptation any credit as required by Section 286 | 4(b), as requested.
  2. 287 | 288 |
  3. If You Distribute, or Publicly Perform the Work or 289 | any Adaptations or Collections, You must, unless a 290 | request has been made pursuant to Section 4(a), keep 291 | intact all copyright notices for the Work and provide, 292 | reasonable to the medium or means You are utilizing: (i) 293 | the name of the Original Author (or pseudonym, if 294 | applicable) if supplied, and/or if the Original Author 295 | and/or Licensor designate another party or parties (e.g., 296 | a sponsor institute, publishing entity, journal) for 297 | attribution ("Attribution Parties") in Licensor's 298 | copyright notice, terms of service or by other reasonable 299 | means, the name of such party or parties; (ii) the title 300 | of the Work if supplied; (iii) to the extent reasonably 301 | practicable, the URI, if any, that Licensor specifies to 302 | be associated with the Work, unless such URI does not 303 | refer to the copyright notice or licensing information 304 | for the Work; and (iv) , consistent with Section 3(b), in 305 | the case of an Adaptation, a credit identifying the use 306 | of the Work in the Adaptation (e.g., "French translation 307 | of the Work by Original Author," or "Screenplay based on 308 | original Work by Original Author"). The credit required 309 | by this Section 4 (b) may be implemented in any 310 | reasonable manner; provided, however, that in the case of 311 | a Adaptation or Collection, at a minimum such credit will 312 | appear, if a credit for all contributing authors of the 313 | Adaptation or Collection appears, then as part of these 314 | credits and in a manner at least as prominent as the 315 | credits for the other contributing authors. For the 316 | avoidance of doubt, You may only use the credit required 317 | by this Section for the purpose of attribution in the 318 | manner set out above and, by exercising Your rights under 319 | this License, You may not implicitly or explicitly assert 320 | or imply any connection with, sponsorship or endorsement 321 | by the Original Author, Licensor and/or Attribution 322 | Parties, as appropriate, of You or Your use of the Work, 323 | without the separate, express prior written permission of 324 | the Original Author, Licensor and/or Attribution 325 | Parties.
  4. 326 | 327 |
  5. Except as otherwise agreed in writing by the Licensor 328 | or as may be otherwise permitted by applicable law, if 329 | You Reproduce, Distribute or Publicly Perform the Work 330 | either by itself or as part of any Adaptations or 331 | Collections, You must not distort, mutilate, modify or 332 | take other derogatory action in relation to the Work 333 | which would be prejudicial to the Original Author's honor 334 | or reputation. Licensor agrees that in those 335 | jurisdictions (e.g. Japan), in which any exercise of the 336 | right granted in Section 3(b) of this License (the right 337 | to make Adaptations) would be deemed to be a distortion, 338 | mutilation, modification or other derogatory action 339 | prejudicial to the Original Author's honor and 340 | reputation, the Licensor will waive or not assert, as 341 | appropriate, this Section, to the fullest extent 342 | permitted by the applicable national law, to enable You 343 | to reasonably exercise Your right under Section 3(b) of 344 | this License (right to make Adaptations) but not 345 | otherwise.
  6. 346 |
347 | 348 |

5. Representations, Warranties and 349 | Disclaimer

350 | 351 |

UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN 352 | WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO 353 | REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE 354 | WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, 355 | WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, 356 | FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE 357 | ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE 358 | PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. 359 | SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED 360 | WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

361 | 362 |

6. Limitation on Liability. EXCEPT TO 363 | THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL 364 | LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY 365 | SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY 366 | DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, 367 | EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF 368 | SUCH DAMAGES.

369 | 370 |

7. Termination

371 | 372 |
    373 |
  1. This License and the rights granted hereunder will 374 | terminate automatically upon any breach by You of the 375 | terms of this License. Individuals or entities who have 376 | received Adaptations or Collections from You under this 377 | License, however, will not have their licenses terminated 378 | provided such individuals or entities remain in full 379 | compliance with those licenses. Sections 1, 2, 5, 6, 7, 380 | and 8 will survive any termination of this License.
  2. 381 | 382 |
  3. Subject to the above terms and conditions, the 383 | license granted here is perpetual (for the duration of 384 | the applicable copyright in the Work). Notwithstanding 385 | the above, Licensor reserves the right to release the 386 | Work under different license terms or to stop 387 | distributing the Work at any time; provided, however that 388 | any such election will not serve to withdraw this License 389 | (or any other license that has been, or is required to 390 | be, granted under the terms of this License), and this 391 | License will continue in full force and effect unless 392 | terminated as stated above.
  4. 393 |
394 | 395 |

8. Miscellaneous

396 | 397 |
    398 |
  1. Each time You Distribute or Publicly Perform the Work 399 | or a Collection, the Licensor offers to the recipient a 400 | license to the Work on the same terms and conditions as 401 | the license granted to You under this License.
  2. 402 | 403 |
  3. Each time You Distribute or Publicly Perform an 404 | Adaptation, Licensor offers to the recipient a license to 405 | the original Work on the same terms and conditions as the 406 | license granted to You under this License.
  4. 407 | 408 |
  5. If any provision of this License is invalid or 409 | unenforceable under applicable law, it shall not affect 410 | the validity or enforceability of the remainder of the 411 | terms of this License, and without further action by the 412 | parties to this agreement, such provision shall be 413 | reformed to the minimum extent necessary to make such 414 | provision valid and enforceable.
  6. 415 | 416 |
  7. No term or provision of this License shall be deemed 417 | waived and no breach consented to unless such waiver or 418 | consent shall be in writing and signed by the party to be 419 | charged with such waiver or consent.
  8. 420 | 421 |
  9. This License constitutes the entire agreement between 422 | the parties with respect to the Work licensed here. There 423 | are no understandings, agreements or representations with 424 | respect to the Work not specified here. Licensor shall 425 | not be bound by any additional provisions that may appear 426 | in any communication from You. This License may not be 427 | modified without the mutual written agreement of the 428 | Licensor and You.
  10. 429 | 430 |
  11. The rights granted under, and the subject matter 431 | referenced, in this License were drafted utilizing the 432 | terminology of the Berne Convention for the Protection of 433 | Literary and Artistic Works (as amended on September 28, 434 | 1979), the Rome Convention of 1961, the WIPO Copyright 435 | Treaty of 1996, the WIPO Performances and Phonograms 436 | Treaty of 1996 and the Universal Copyright Convention (as 437 | revised on July 24, 1971). These rights and subject 438 | matter take effect in the relevant jurisdiction in which 439 | the License terms are sought to be enforced according to 440 | the corresponding provisions of the implementation of 441 | those treaty provisions in the applicable national law. 442 | If the standard suite of rights granted under applicable 443 | copyright law includes additional rights not granted 444 | under this License, such additional rights are deemed to 445 | be included in the License; this License is not intended 446 | to restrict the license of any rights under applicable 447 | law.
  12. 448 |
449 | 450 | 451 |
452 |

Creative Commons Notice

453 | 454 |

Creative Commons is not a party to this License, and 455 | makes no warranty whatsoever in connection with the Work. 456 | Creative Commons will not be liable to You or any party 457 | on any legal theory for any damages whatsoever, including 458 | without limitation any general, special, incidental or 459 | consequential damages arising in connection to this 460 | license. Notwithstanding the foregoing two (2) sentences, 461 | if Creative Commons has expressly identified itself as 462 | the Licensor hereunder, it shall have all rights and 463 | obligations of Licensor.

464 | 465 |

Except for the limited purpose of indicating to the 466 | public that the Work is licensed under the CCPL, Creative 467 | Commons does not authorize the use by either party of the 468 | trademark "Creative Commons" or any related trademark or 469 | logo of Creative Commons without the prior written 470 | consent of Creative Commons. Any permitted use will be in 471 | compliance with Creative Commons' then-current trademark 472 | usage guidelines, as may be published on its website or 473 | otherwise made available upon request from time to time. 474 | For the avoidance of doubt, this trademark restriction 475 | does not form part of this License.

476 | 477 |

Creative Commons may be contacted at http://creativecommons.org/.

479 |
480 |
481 |
482 | 483 | 486 |
487 | 488 | 489 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/GenericPinAnnotationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // GenericPinAnnotationView.h 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | 15 | @interface GenericPinAnnotationView : MKPinAnnotationView 16 | + (instancetype)pinViewWithAnnotation:(NSObject *)annotation; 17 | @property (nonatomic,assign) BOOL preventSelectionChange; 18 | @end 19 | 20 | extern NSString* const GenericPinReuseIdentifier; -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/GenericPinAnnotationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // GenericPinAnnotationView.m 3 | // Created by Gregory Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "GenericPinAnnotationView.h" 14 | 15 | NSString* const GenericPinReuseIdentifier = @"GenericPinReuse"; 16 | 17 | @implementation GenericPinAnnotationView 18 | 19 | + (instancetype)pinViewWithAnnotation:(NSObject *)annotation 20 | { 21 | return [[GenericPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:GenericPinReuseIdentifier]; 22 | } 23 | 24 | - (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier 25 | { 26 | self = [super initWithAnnotation:annotation reuseIdentifier:reuseIdentifier]; 27 | if (self) 28 | { 29 | self.canShowCallout = NO; 30 | } 31 | return self; 32 | } 33 | 34 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated 35 | { 36 | if (!_preventSelectionChange) 37 | { 38 | [super setSelected:selected animated: animated]; 39 | } 40 | } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowAnnotation.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowAnnotation.h 3 | // Created by Greg Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "MultiRowAnnotationProtocol.h" 14 | 15 | @interface MultiRowAnnotation : NSObject 16 | @property (nonatomic,assign) CLLocationCoordinate2D coordinate; 17 | @property (nonatomic,copy) NSString *title; 18 | @property (nonatomic,copy) NSArray *calloutCells; // MultiRowCalloutCells 19 | 20 | + (instancetype)annotationWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title calloutCells:(NSArray *)calloutCells; 21 | - (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title calloutCells:(NSArray *)calloutCells NS_DESIGNATED_INITIALIZER; 22 | - (void)copyAttributesFromAnnotation:(NSObject *)annotation; 23 | @end 24 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowAnnotation.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowAnnotation.m 3 | // Created by Greg Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "MultiRowAnnotation.h" 14 | 15 | @implementation MultiRowAnnotation 16 | 17 | + (instancetype)annotationWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title calloutCells:(NSArray *)calloutCells 18 | { 19 | return [[MultiRowAnnotation alloc] initWithCoordinate:coordinate title:title calloutCells:calloutCells]; 20 | } 21 | 22 | - (instancetype)initWithCoordinate:(CLLocationCoordinate2D)coordinate title:(NSString *)title calloutCells:(NSArray *)calloutCells 23 | { 24 | self = [super init]; 25 | if (self) 26 | { 27 | _coordinate = coordinate; 28 | _title = title; 29 | _calloutCells = calloutCells; 30 | } 31 | return self; 32 | } 33 | 34 | 35 | // For selection/deselection of the callout in the map view controller, we need to make a copy of the annotation 36 | - (id)copyWithZone:(NSZone *)zone 37 | { 38 | return [[MultiRowAnnotation allocWithZone:zone] initWithCoordinate:_coordinate title:_title calloutCells:_calloutCells]; 39 | } 40 | 41 | - (void)copyAttributesFromAnnotation:(NSObject *)annotation 42 | { 43 | _coordinate = annotation.coordinate; 44 | _title = [annotation.title copy]; 45 | _calloutCells = [annotation calloutCells]; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowAnnotationProtocol.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowAnnotationProtocol.h 3 | // Created by Greg Combs on 11/30/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | 15 | @protocol MultiRowAnnotationProtocol 16 | @required 17 | - (CLLocationCoordinate2D) coordinate; 18 | - (NSString *)title; 19 | - (NSArray *)calloutCells; // MultiRowCalloutCells 20 | @end 21 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowCalloutAnnotationView.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowCalloutAnnotationView.h 3 | // Created by Greg Combs on 11/29/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // A portion of this class is based on James Rantanen's work at Asynchrony Solutions 8 | // http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/ 9 | // http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-2/ 10 | // 11 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 12 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 13 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 14 | // 15 | // 16 | 17 | #import "MultiRowAnnotationProtocol.h" 18 | #import "MultiRowCalloutCell.h" 19 | 20 | @interface MultiRowCalloutAnnotationView : MKAnnotationView 21 | + (instancetype)calloutWithAnnotation:(id)annotation onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block; 22 | - (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block NS_DESIGNATED_INITIALIZER; 23 | /* Callout cells are MultiRowCalloutCells. If the annotation object responds to "calloutCells", 24 | this will be set automatically upon initialization */ 25 | @property (nonatomic,strong) NSArray *calloutCells; 26 | @property (nonatomic,copy) MultiRowAccessoryTappedBlock onCalloutAccessoryTapped; // copied to cells 27 | @property (nonatomic,strong) MKAnnotationView *parentAnnotationView; 28 | @property (nonatomic,unsafe_unretained) MKMapView *mapView; 29 | @end 30 | 31 | extern NSString* const MultiRowCalloutReuseIdentifier; 32 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowCalloutAnnotationView.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowCalloutAnnotationView.m 3 | // Created by Greg Combs on 11/29/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // A portion of this class is based on James Rantanen's work at Asynchrony Solutions 8 | // http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/ 9 | // http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-2/ 10 | // 11 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 12 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 13 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 14 | // 15 | // 16 | 17 | #import "MultiRowCalloutAnnotationView.h" 18 | #import "GenericPinAnnotationView.h" 19 | #import 20 | #import 21 | 22 | NSString* const MultiRowCalloutReuseIdentifier = @"MultiRowCalloutReuse"; 23 | CGFloat const kMultiRowCalloutCellGap = 3; 24 | 25 | @interface MultiRowCalloutAnnotationView() 26 | @property (nonatomic,strong) IBOutlet UILabel *titleLabel; 27 | @property (nonatomic,assign) CGFloat cellInsetX; 28 | @property (nonatomic,assign) CGFloat cellOffsetY; 29 | @property (nonatomic,assign) CGFloat contentHeight; 30 | @property (nonatomic,assign) CGPoint offsetFromParent; 31 | @property (nonatomic,readonly) CGFloat yShadowOffset; 32 | @property (nonatomic,strong) UIView *contentView; 33 | @property (nonatomic,readonly) CGSize actualSize; // contentSize + buffers 34 | @property (nonatomic,assign) BOOL animateOnNextDrawRect; 35 | @property (nonatomic,assign) CGRect endFrame; 36 | @property (nonatomic,assign) CGFloat xPixelShift; 37 | 38 | @end 39 | 40 | @implementation MultiRowCalloutAnnotationView 41 | 42 | + (MultiRowCalloutAnnotationView *)calloutWithAnnotation:(id)annotation onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block 43 | { 44 | return [[MultiRowCalloutAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:MultiRowCalloutReuseIdentifier onCalloutAccessoryTapped:block]; 45 | } 46 | 47 | - (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block { 48 | self = [super initWithAnnotation:(id)annotation reuseIdentifier:reuseIdentifier]; 49 | if (self) 50 | { 51 | _contentHeight = 80.0; 52 | _yShadowOffset = 6; 53 | _offsetFromParent = CGPointMake(8, -14); //this works for MKPinAnnotationView 54 | _cellInsetX = 15; 55 | _cellOffsetY = 10; 56 | _onCalloutAccessoryTapped = block; 57 | self.enabled = NO; 58 | self.backgroundColor = [UIColor clearColor]; 59 | [self setTitleWithAnnotation:annotation]; 60 | [self setCalloutCellsWithAnnotation:annotation]; 61 | } 62 | return self; 63 | } 64 | 65 | - (instancetype)initWithAnnotation:(id)annotation reuseIdentifier:(NSString *)reuseIdentifier { 66 | self = [self initWithAnnotation:annotation reuseIdentifier:reuseIdentifier onCalloutAccessoryTapped:nil]; 67 | return self; 68 | } 69 | 70 | - (void)dealloc 71 | { 72 | self.calloutCells = nil; 73 | self.mapView = nil; 74 | } 75 | 76 | #pragma mark - Setters and Accessors 77 | 78 | - (void)setAnnotation:(id )annotation 79 | { 80 | [super setAnnotation:annotation]; 81 | if (!annotation) 82 | { 83 | return; 84 | } 85 | [self setTitleWithAnnotation:(id)annotation]; 86 | [self setCalloutCellsWithAnnotation:(id)annotation]; 87 | [self prepareFrameSize]; 88 | [self prepareOffset]; 89 | [self prepareContentFrame]; 90 | [self setNeedsDisplay]; 91 | } 92 | 93 | - (void)setTitleWithAnnotation:(id)annotation 94 | { 95 | if (!_titleLabel) 96 | { 97 | _titleLabel = [[UILabel alloc] init]; 98 | _titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; 99 | _titleLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth; 100 | _titleLabel.textColor = [UIColor colorWithRed:242/255.f green:245/255.f blue:226/255.f alpha:1]; 101 | _titleLabel.backgroundColor = [UIColor clearColor]; 102 | _titleLabel.font = [UIFont boldSystemFontOfSize:14]; 103 | _titleLabel.shadowColor = [UIColor darkTextColor]; 104 | _titleLabel.shadowOffset = CGSizeMake(0, -1); 105 | [self.contentView addSubview:_titleLabel]; 106 | } 107 | if (annotation) 108 | { 109 | _cellOffsetY = 35 + (2*kMultiRowCalloutCellGap); 110 | _titleLabel.text = annotation.title; 111 | _titleLabel.hidden = NO; 112 | } 113 | else { 114 | _titleLabel.hidden = YES; 115 | _cellOffsetY = 10; 116 | } 117 | } 118 | 119 | - (void)setCalloutCellsWithAnnotation:(id)annotation 120 | { 121 | if (annotation) 122 | { 123 | [self setCalloutCells:[annotation calloutCells]]; 124 | } 125 | } 126 | 127 | - (void)setCalloutCells:(NSArray *)calloutCells 128 | { 129 | if (_calloutCells) 130 | { 131 | for (UIView *cell in _calloutCells) 132 | { 133 | [cell removeFromSuperview]; 134 | } 135 | } 136 | _calloutCells = calloutCells; 137 | if (calloutCells) 138 | { 139 | _contentHeight = _cellOffsetY + ([calloutCells count] * (kMultiRowCalloutCellSize.height + kMultiRowCalloutCellGap)); 140 | for (UIView *cell in calloutCells) 141 | { 142 | [self.contentView addSubview:cell]; 143 | } 144 | [self prepareContentFrame]; 145 | [self copyAccessoryTappedBlockToCalloutCells]; 146 | } 147 | } 148 | 149 | #pragma mark - Block setters 150 | 151 | - (void)setOnCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)onCalloutAccessoryTapped 152 | { 153 | _onCalloutAccessoryTapped = onCalloutAccessoryTapped; 154 | [self copyAccessoryTappedBlockToCalloutCells]; 155 | } 156 | 157 | - (void)copyAccessoryTappedBlockToCalloutCells 158 | { 159 | if (!_onCalloutAccessoryTapped) 160 | return; 161 | for (MultiRowCalloutCell *cell in _calloutCells) 162 | { 163 | if (!cell.onCalloutAccessoryTapped) 164 | { 165 | cell.onCalloutAccessoryTapped = _onCalloutAccessoryTapped; 166 | } 167 | } 168 | } 169 | 170 | #pragma mark - Layout 171 | 172 | - (void)prepareContentFrame 173 | { 174 | CGRect contentRect = CGRectOffset(self.bounds, 10, 3); 175 | contentRect.size = [self contentSize]; 176 | self.contentView.frame = contentRect; 177 | 178 | if (_titleLabel) 179 | { 180 | _titleLabel.frame = CGRectMake(_cellInsetX, 10, kMultiRowCalloutCellSize.width - (2*_cellInsetX), 25); 181 | } 182 | NSInteger index = 0; 183 | for (MultiRowCalloutCell *cell in self.calloutCells) 184 | { 185 | cell.frame = CGRectMake(_cellInsetX, _cellOffsetY + index * (kMultiRowCalloutCellSize.height+kMultiRowCalloutCellGap), kMultiRowCalloutCellSize.width - (_cellInsetX*2), kMultiRowCalloutCellSize.height - (2*kMultiRowCalloutCellGap)); 186 | [cell setNeedsDisplay]; 187 | index++; 188 | } 189 | } 190 | 191 | - (CGSize)contentSize 192 | { 193 | return CGSizeMake(kMultiRowCalloutCellSize.width, _contentHeight); 194 | } 195 | 196 | #pragma mark - Selection/Deselection 197 | 198 | - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event 199 | { 200 | UIView *hitView = [super hitTest:point withEvent:event]; 201 | //If the accessory is hit, the map view may want to select an annotation sitting below it, so we must disable the other annotations ... But not the parent because that will screw up the selection 202 | if ([hitView isKindOfClass:[UIButton class]]) 203 | { 204 | [self preventParentSelectionChange]; 205 | __weak __typeof__(self) bself = self; 206 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 207 | [bself allowParentSelectionChange]; 208 | }); 209 | 210 | for (UIView *aView in self.superview.subviews) 211 | { 212 | if (![aView isKindOfClass:[MKAnnotationView class]] || 213 | aView == self.parentAnnotationView) 214 | { 215 | continue; 216 | } 217 | MKAnnotationView *sibling = (MKAnnotationView *)aView; 218 | sibling.enabled = NO; 219 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 220 | sibling.enabled = YES; 221 | }); 222 | } 223 | } 224 | return hitView; 225 | } 226 | 227 | - (void)preventParentSelectionChange 228 | { 229 | if (self.parentAnnotationView && 230 | [self.parentAnnotationView respondsToSelector:@selector(setPreventSelectionChange:)]) 231 | { 232 | GenericPinAnnotationView *parentView = (GenericPinAnnotationView *)self.parentAnnotationView; 233 | parentView.preventSelectionChange = YES; 234 | } 235 | } 236 | 237 | - (void)allowParentSelectionChange 238 | { 239 | if (!self.mapView || !self.parentAnnotationView) 240 | return; 241 | //The MapView may think it has deselected the pin, so we should re-select it 242 | [self.mapView selectAnnotation:self.parentAnnotationView.annotation animated:NO]; 243 | if ([self.parentAnnotationView respondsToSelector:@selector(setPreventSelectionChange:)]) 244 | { 245 | GenericPinAnnotationView *parentView = (GenericPinAnnotationView *)self.parentAnnotationView; 246 | parentView.preventSelectionChange = NO; 247 | } 248 | } 249 | 250 | #pragma mark - Abstract Class Methods 251 | 252 | #define CalloutMapAnnotationViewBottomShadowBufferSize 6.0f 253 | #define CalloutMapAnnotationViewContentHeightBuffer 8.0f 254 | #define CalloutMapAnnotationViewHeightAboveParent 2.0f 255 | 256 | - (void)didMoveToSuperview 257 | { 258 | [super didMoveToSuperview]; 259 | if (!self.mapView || !self.superview) 260 | return; // superview can/will be nil during deallocation 261 | [self adjustMapRegionIfNeeded]; 262 | [self animateIn]; 263 | [self setNeedsLayout]; 264 | } 265 | 266 | - (CGSize)actualSize 267 | { 268 | CGSize size = [self contentSize]; 269 | size.width += 20; 270 | size.height += (CalloutMapAnnotationViewContentHeightBuffer + 271 | CalloutMapAnnotationViewBottomShadowBufferSize - 272 | self.offsetFromParent.y); 273 | return size; 274 | } 275 | 276 | - (void)prepareFrameSize 277 | { 278 | CGRect frame = self.frame; 279 | frame.size = [self actualSize]; 280 | self.frame = frame; 281 | } 282 | 283 | - (void)prepareOffset 284 | { 285 | CGPoint parentOrigin = [self.mapView convertPoint:self.parentAnnotationView.frame.origin fromView:self.parentAnnotationView.superview]; 286 | CGFloat xOffset = (self.actualSize.width / 2) - (parentOrigin.x + self.offsetFromParent.x); 287 | //Add half our height plus half of the height of the annotation we are tied to so that our bottom lines up to its top 288 | //Then take into account its offset and the extra space needed for our drop shadow 289 | CGFloat yOffset = -(self.frame.size.height / 2 + self.parentAnnotationView.frame.size.height / 2) + self.offsetFromParent.y + CalloutMapAnnotationViewBottomShadowBufferSize; 290 | self.centerOffset = CGPointMake(xOffset, yOffset); 291 | } 292 | 293 | //if the pin is too close to the edge of the map view we need to shift the map view so the callout will fit. 294 | - (void)adjustMapRegionIfNeeded 295 | { 296 | if (!self.mapView) 297 | return; 298 | 299 | //Longitude 300 | self.xPixelShift = 0; 301 | if ([self relativeParentXPosition] < 38) 302 | { 303 | self.xPixelShift = 38 - [self relativeParentXPosition]; 304 | } 305 | else if ([self relativeParentXPosition] > self.frame.size.width - 38) 306 | { 307 | self.xPixelShift = (self.frame.size.width - 38) - [self relativeParentXPosition]; 308 | } 309 | 310 | //Latitude 311 | CGPoint mapViewOriginRelativeToParent = [self.mapView convertPoint:self.mapView.frame.origin toView:self.parentAnnotationView]; 312 | CGFloat yPixelShift = 0; 313 | CGFloat pixelsFromTopOfMapView = -(mapViewOriginRelativeToParent.y + self.frame.size.height - CalloutMapAnnotationViewBottomShadowBufferSize); 314 | CGFloat pixelsFromBottomOfMapView = self.mapView.frame.size.height + mapViewOriginRelativeToParent.y - self.parentAnnotationView.frame.size.height; 315 | if (pixelsFromTopOfMapView < 7) 316 | { 317 | yPixelShift = 7 - pixelsFromTopOfMapView; 318 | } 319 | else if (pixelsFromBottomOfMapView < 10) 320 | { 321 | yPixelShift = -(10 - pixelsFromBottomOfMapView); 322 | } 323 | 324 | //Calculate new center point, if needed 325 | if (self.xPixelShift || yPixelShift) 326 | { 327 | CGFloat pixelsPerDegreeLongitude = self.mapView.frame.size.width / self.mapView.region.span.longitudeDelta; 328 | CGFloat pixelsPerDegreeLatitude = self.mapView.frame.size.height / self.mapView.region.span.latitudeDelta; 329 | 330 | CLLocationDegrees longitudinalShift = -(self.xPixelShift / pixelsPerDegreeLongitude); 331 | CLLocationDegrees latitudinalShift = yPixelShift / pixelsPerDegreeLatitude; 332 | 333 | CLLocationCoordinate2D newCenterCoordinate = {self.mapView.region.center.latitude + latitudinalShift, self.mapView.region.center.longitude + longitudinalShift}; 334 | 335 | @try { 336 | if (CLLocationCoordinate2DIsValid(newCenterCoordinate)) 337 | { 338 | [self.mapView setCenterCoordinate:newCenterCoordinate animated:YES]; 339 | } 340 | } 341 | @catch (NSException *exception) { 342 | NSLog(@"MultiRowCalloutAnnotationView: An elusive error occurred in adjustMapRegionIfNeeded() while setting mapview's center coordinate. Coordinates: lat=%lf long=%lf ... mapview = %@", newCenterCoordinate.latitude, newCenterCoordinate.longitude, self.mapView); 343 | if (exception) 344 | { 345 | NSLog(@"Exception: %@", exception); 346 | } 347 | } 348 | 349 | //fix for now 350 | self.frame = CGRectOffset(self.frame, -self.xPixelShift, -yPixelShift); 351 | //fix for later (after zoom or other action that resets the frame) 352 | self.centerOffset = CGPointMake(self.centerOffset.x - self.xPixelShift, self.centerOffset.y); 353 | } 354 | } 355 | 356 | - (CGFloat)xTransformForScale:(CGFloat)scale 357 | { 358 | CGFloat xDistanceFromCenterToParent = (self.endFrame.size.width / 2) - [self relativeParentXPosition]; 359 | return (xDistanceFromCenterToParent * scale) - xDistanceFromCenterToParent; 360 | } 361 | 362 | - (CGFloat)yTransformForScale:(CGFloat)scale 363 | { 364 | CGFloat yDistanceFromCenterToParent = ((self.endFrame.size.height / 2) + self.offsetFromParent.y + CalloutMapAnnotationViewBottomShadowBufferSize + CalloutMapAnnotationViewHeightAboveParent); 365 | return yDistanceFromCenterToParent - yDistanceFromCenterToParent * scale; 366 | } 367 | 368 | - (void)animateIn 369 | { 370 | self.endFrame = self.frame; 371 | CGFloat scale = 0.001f; 372 | self.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale], [self yTransformForScale:scale]); 373 | [UIView beginAnimations:@"animateIn" context:nil]; 374 | [UIView setAnimationCurve:UIViewAnimationCurveEaseOut]; 375 | [UIView setAnimationDuration:0.075]; 376 | [UIView setAnimationDidStopSelector:@selector(animateInStepTwo)]; 377 | [UIView setAnimationDelegate:self]; 378 | scale = 1.1; 379 | self.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale], [self yTransformForScale:scale]); 380 | [UIView commitAnimations]; 381 | } 382 | 383 | - (void)animateInStepTwo 384 | { 385 | [UIView beginAnimations:@"animateInStepTwo" context:nil]; 386 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 387 | [UIView setAnimationDuration:0.1]; 388 | [UIView setAnimationDidStopSelector:@selector(animateInStepThree)]; 389 | [UIView setAnimationDelegate:self]; 390 | CGFloat scale = 0.95; 391 | self.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale], [self yTransformForScale:scale]); 392 | [UIView commitAnimations]; 393 | } 394 | 395 | - (void)animateInStepThree 396 | { 397 | [UIView beginAnimations:@"animateInStepThree" context:nil]; 398 | [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; 399 | [UIView setAnimationDuration:0.075]; 400 | CGFloat scale = 1.0; 401 | self.transform = CGAffineTransformMake(scale, 0.0f, 0.0f, scale, [self xTransformForScale:scale], [self yTransformForScale:scale]); 402 | [UIView commitAnimations]; 403 | } 404 | 405 | - (void)drawRect:(CGRect)rect 406 | { 407 | [super drawRect:rect]; 408 | CGFloat stroke = 1.0; 409 | CGFloat radius = 7.0; 410 | CGMutablePathRef path = CGPathCreateMutable(); 411 | UIColor *color; 412 | CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); 413 | CGContextRef context = UIGraphicsGetCurrentContext(); 414 | CGFloat parentX = [self relativeParentXPosition]; 415 | //Determine Size 416 | rect = self.bounds; 417 | rect.size.width -= stroke + 14; 418 | rect.size.height -= stroke + CalloutMapAnnotationViewHeightAboveParent - self.offsetFromParent.y + CalloutMapAnnotationViewBottomShadowBufferSize; 419 | rect.origin.x += stroke / 2.0 + 7; 420 | rect.origin.y += stroke / 2.0; 421 | 422 | //Create Path For Callout Bubble 423 | CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y + radius); 424 | CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.origin.y + rect.size.height - radius); 425 | CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + rect.size.height - radius, radius, M_PI, M_PI / 2, 1); 426 | CGPathAddLineToPoint(path, NULL, parentX - 15, rect.origin.y + rect.size.height); 427 | CGPathAddLineToPoint(path, NULL, parentX, rect.origin.y + rect.size.height + 15); 428 | CGPathAddLineToPoint(path, NULL, parentX + 15, rect.origin.y + rect.size.height); 429 | CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height); 430 | CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + rect.size.height - radius, radius, M_PI / 2, 0.0f, 1); 431 | CGPathAddLineToPoint(path, NULL, rect.origin.x + rect.size.width, rect.origin.y + radius); 432 | CGPathAddArc(path, NULL, rect.origin.x + rect.size.width - radius, rect.origin.y + radius, radius, 0.0f, -M_PI / 2, 1); 433 | CGPathAddLineToPoint(path, NULL, rect.origin.x + radius, rect.origin.y); 434 | CGPathAddArc(path, NULL, rect.origin.x + radius, rect.origin.y + radius, radius, -M_PI / 2, M_PI, 1); 435 | CGPathCloseSubpath(path); 436 | 437 | //Fill Callout Bubble & Add Shadow 438 | color = [[UIColor blackColor] colorWithAlphaComponent:.6]; 439 | [color setFill]; 440 | CGContextAddPath(context, path); 441 | CGContextSaveGState(context); 442 | CGContextSetShadowWithColor(context, CGSizeMake (0, self.yShadowOffset), 6, [UIColor colorWithWhite:0 alpha:.5].CGColor); 443 | CGContextFillPath(context); 444 | CGContextRestoreGState(context); 445 | 446 | //Stroke Callout Bubble 447 | color = [[UIColor darkGrayColor] colorWithAlphaComponent:.9]; 448 | [color setStroke]; 449 | CGContextSetLineWidth(context, stroke); 450 | CGContextSetLineCap(context, kCGLineCapSquare); 451 | CGContextAddPath(context, path); 452 | CGContextStrokePath(context); 453 | 454 | //Determine Size for Gloss 455 | CGRect glossRect = self.bounds; 456 | glossRect.size.width = rect.size.width - stroke; 457 | glossRect.size.height = (rect.size.height - stroke) / 2; 458 | glossRect.origin.x = rect.origin.x + stroke / 2; 459 | glossRect.origin.y += rect.origin.y + stroke / 2; 460 | 461 | CGFloat glossTopRadius = radius - stroke / 2; 462 | CGFloat glossBottomRadius = radius / 1.5; 463 | 464 | //Create Path For Gloss 465 | CGMutablePathRef glossPath = CGPathCreateMutable(); 466 | CGPathMoveToPoint(glossPath, NULL, glossRect.origin.x, glossRect.origin.y + glossTopRadius); 467 | CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x, glossRect.origin.y + glossRect.size.height - glossBottomRadius); 468 | CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossBottomRadius, glossRect.origin.y + glossRect.size.height - glossBottomRadius, glossBottomRadius, M_PI, M_PI / 2, 1); 469 | CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x + glossRect.size.width - glossBottomRadius, glossRect.origin.y + glossRect.size.height); 470 | CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossRect.size.width - glossBottomRadius, glossRect.origin.y + glossRect.size.height - glossBottomRadius, glossBottomRadius, M_PI / 2, 0.0f, 1); 471 | CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x + glossRect.size.width, glossRect.origin.y + glossTopRadius); 472 | CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossRect.size.width - glossTopRadius, glossRect.origin.y + glossTopRadius, glossTopRadius, 0.0f, -M_PI / 2, 1); 473 | CGPathAddLineToPoint(glossPath, NULL, glossRect.origin.x + glossTopRadius, glossRect.origin.y); 474 | CGPathAddArc(glossPath, NULL, glossRect.origin.x + glossTopRadius, glossRect.origin.y + glossTopRadius, glossTopRadius, -M_PI / 2, M_PI, 1); 475 | CGPathCloseSubpath(glossPath); 476 | 477 | //Fill Gloss Path 478 | CGContextAddPath(context, glossPath); 479 | CGContextClip(context); 480 | CGFloat colors[] = 481 | { 482 | 1, 1, 1, .3, 483 | 1, 1, 1, .1, 484 | }; 485 | CGFloat locations[] = { 0, 1.0 }; 486 | CGGradientRef gradient = CGGradientCreateWithColorComponents(space, colors, locations, 2); 487 | CGPoint startPoint = glossRect.origin; 488 | CGPoint endPoint = CGPointMake(glossRect.origin.x, glossRect.origin.y + glossRect.size.height); 489 | CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); 490 | 491 | //Gradient Stroke Gloss Path 492 | CGContextAddPath(context, glossPath); 493 | CGContextSetLineWidth(context, 2); 494 | CGContextReplacePathWithStrokedPath(context); 495 | CGContextClip(context); 496 | CGFloat colors2[] = 497 | { 498 | 1, 1, 1, .3, 499 | 1, 1, 1, .1, 500 | 1, 1, 1, .0, 501 | }; 502 | CGFloat locations2[] = { 0, .1, 1.0 }; 503 | CGGradientRef gradient2 = CGGradientCreateWithColorComponents(space, colors2, locations2, 3); 504 | CGPoint startPoint2 = glossRect.origin; 505 | CGPoint endPoint2 = CGPointMake(glossRect.origin.x, glossRect.origin.y + glossRect.size.height); 506 | CGContextDrawLinearGradient(context, gradient2, startPoint2, endPoint2, 0); 507 | 508 | //Cleanup 509 | CGPathRelease(path); 510 | CGPathRelease(glossPath); 511 | CGColorSpaceRelease(space); 512 | CGGradientRelease(gradient); 513 | CGGradientRelease(gradient2); 514 | } 515 | 516 | - (CGFloat)relativeParentXPosition 517 | { 518 | if (!_mapView || !_parentAnnotationView) 519 | return 0; 520 | CGPoint parentOrigin = [self.mapView convertPoint:self.parentAnnotationView.frame.origin fromView:self.parentAnnotationView.superview]; 521 | return parentOrigin.x + self.offsetFromParent.x + self.xPixelShift; 522 | } 523 | 524 | - (UIView *)contentView 525 | { 526 | if (!_contentView) 527 | { 528 | _contentView = [[UIView alloc] init]; 529 | _contentView.backgroundColor = [UIColor clearColor]; 530 | _contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 531 | [self addSubview:_contentView]; 532 | } 533 | return _contentView; 534 | } 535 | 536 | @end 537 | -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowCalloutCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowCalloutCell.h 3 | // Created by Greg Combs on 11/29/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import 14 | #import 15 | 16 | @class MultiRowCalloutCell; 17 | typedef void(^MultiRowAccessoryTappedBlock)(MultiRowCalloutCell *cell, UIControl *control, NSDictionary *userData); 18 | 19 | @interface MultiRowCalloutCell : UITableViewCell 20 | @property (nonatomic,strong) NSDictionary *userData; 21 | @property (nonatomic,copy) MultiRowAccessoryTappedBlock onCalloutAccessoryTapped; 22 | @property (nonatomic,unsafe_unretained) NSString *title; 23 | @property (nonatomic,unsafe_unretained) NSString *subtitle; 24 | @property (nonatomic,unsafe_unretained) UIImage *image; 25 | + (instancetype)cellWithImage:(UIImage *)image title:(NSString *)title subtitle:(NSString *)subtitle userData:(NSDictionary *)userData; 26 | + (instancetype)cellWithImage:(UIImage *)image title:(NSString *)title subtitle:(NSString *)subtitle userData:(NSDictionary *)userData onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block; 27 | @end 28 | 29 | extern CGSize const kMultiRowCalloutCellSize; -------------------------------------------------------------------------------- /MultiRowCalloutAnnotationView/MultiRowCalloutCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // MultiRowCalloutCell.m 3 | // Created by Greg Combs on 11/29/11. 4 | // 5 | // based on work at https://github.com/grgcombs/MultiRowCalloutAnnotationView 6 | // 7 | // This work is licensed under the Creative Commons Attribution 3.0 Unported License. 8 | // To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/ 9 | // or send a letter to Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA. 10 | // 11 | // 12 | 13 | #import "MultiRowCalloutCell.h" 14 | #import 15 | 16 | CGSize const kMultiRowCalloutCellSize = {245,44}; 17 | 18 | @interface MultiRowCalloutCell() 19 | - (instancetype)initWithImage:(UIImage *)image title:(NSString *)title subtitle:(NSString *)subtitle userData:(NSDictionary *)userData onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block; 20 | - (IBAction)calloutAccessoryTapped:(id)sender; 21 | @end 22 | 23 | @implementation MultiRowCalloutCell 24 | 25 | + (instancetype)cellWithImage:(UIImage *)image title:(NSString *)title subtitle:(NSString *)subtitle userData:(NSDictionary *)userData onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block 26 | { 27 | return [[MultiRowCalloutCell alloc] initWithImage:image title:title subtitle:subtitle userData:userData onCalloutAccessoryTapped:block]; 28 | } 29 | 30 | + (instancetype)cellWithImage:(UIImage *)image title:(NSString *)title subtitle:(NSString *)subtitle userData:(NSDictionary *)userData 31 | { 32 | return [MultiRowCalloutCell cellWithImage:image title:title subtitle:subtitle userData:userData onCalloutAccessoryTapped:nil]; 33 | } 34 | 35 | - (instancetype)initWithImage:(UIImage *)image title:(NSString *)title subtitle:(NSString *)subtitle userData:(NSDictionary *)userData onCalloutAccessoryTapped:(MultiRowAccessoryTappedBlock)block { 36 | self = [super initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:nil]; 37 | if (self) 38 | { 39 | _onCalloutAccessoryTapped = block; 40 | _userData = userData; 41 | self.title = title; 42 | self.subtitle = subtitle; 43 | self.image = image; 44 | self.opaque = YES; 45 | self.backgroundColor = [UIColor colorWithRed:116/255.f green:174/255.f blue:165/255.f alpha:1]; 46 | self.selectionStyle = UITableViewCellSelectionStyleNone; 47 | UIButton *accessory = [UIButton buttonWithType:UIButtonTypeDetailDisclosure]; 48 | accessory.exclusiveTouch = YES; 49 | accessory.enabled = YES; 50 | [accessory addTarget: self action:@selector(calloutAccessoryTapped:) forControlEvents: UIControlEventTouchUpInside | UIControlEventTouchCancel]; 51 | self.accessoryView = accessory; 52 | self.textLabel.backgroundColor = self.backgroundColor; 53 | self.textLabel.textColor = [UIColor colorWithRed:70/255.f green:69/255.f blue:68/255.f alpha:1]; 54 | self.textLabel.font = [UIFont boldSystemFontOfSize:12]; 55 | self.textLabel.shadowColor = [UIColor lightTextColor]; 56 | self.textLabel.shadowOffset = CGSizeMake(0, 1); 57 | self.textLabel.adjustsFontSizeToFitWidth = YES; 58 | self.detailTextLabel.textColor = [UIColor colorWithRed:242/255.f green:245/255.f blue:226/255.f alpha:1]; 59 | self.detailTextLabel.font = [UIFont boldSystemFontOfSize:14]; 60 | self.detailTextLabel.adjustsFontSizeToFitWidth = YES; 61 | self.detailTextLabel.backgroundColor = self.backgroundColor; 62 | self.detailTextLabel.shadowColor = [UIColor darkTextColor]; 63 | self.detailTextLabel.shadowOffset = CGSizeMake(0, -1); 64 | self.layer.cornerRadius = 5; 65 | } 66 | return self; 67 | } 68 | 69 | - (IBAction)calloutAccessoryTapped:(id)sender 70 | { 71 | if (_onCalloutAccessoryTapped) 72 | { 73 | _onCalloutAccessoryTapped(self, sender, _userData); 74 | } 75 | } 76 | 77 | #pragma mark - Convenience Accessors 78 | 79 | - (UIImage *)image 80 | { 81 | return self.imageView.image; 82 | } 83 | 84 | - (void)setImage:(UIImage *)image 85 | { 86 | self.imageView.image = image; 87 | } 88 | 89 | - (NSString *)title 90 | { 91 | return self.textLabel.text; 92 | } 93 | 94 | - (void)setTitle:(NSString *)title 95 | { 96 | self.textLabel.text = title; 97 | } 98 | 99 | - (NSString *)subtitle 100 | { 101 | return self.detailTextLabel.text; 102 | } 103 | 104 | - (void)setSubtitle:(NSString *)subtitle 105 | { 106 | self.detailTextLabel.text = subtitle; 107 | } 108 | 109 | @end 110 | 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MultiRowCalloutAnnotationView - Exactly what it says, for MapKit / iOS 2 | ============= 3 | Created by Gregory S. Combs. 4 | Based on work at [GitHub](https://github.com/grgcombs/MultiRowCalloutAnnotationView). 5 | 6 | Description 7 | ============= 8 | 9 | - This is an annotation view that sports a callout bubble with multiple, independently selectable rows of data. The objective is to allow each cell/row to utilize an accessory disclosure button, without resorting to a more involved UITableViewController scenario. 10 | 11 | Implementation 12 | ============= 13 | 14 | (See the demo for a functional representation of this project.) 15 | 16 | - Presuming you've already set up your project to use the MapKit Framework, you first need to add the appropriate classes in the "MultiRowCalloutAnnotationView" directory. 17 | - Next, ensure your annotation class (if you have a preexisting one) answers to the "title" selector, and that it also returns an array of MultiRowCalloutCells upon request. 18 | - The callout cell class takes a title, subtitle, and an image. You can also supply an NSDictionary for custom data that is passed along on accessory button touches. 19 | - The MultiRowCalloutAnnotationView gathers the necessary information and conveniently uses blocks to handle the button touch events. 20 | 21 | Attributions and Thanks 22 | ============= 23 | 24 | A portion of this class is based on James Rantanen's work at Asynchrony Solutions 25 | 26 | - http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/ 27 | - http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-2/ 28 | 29 | License 30 | ========================= 31 | 32 | [Under a Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/) 33 | 34 | ![Creative Commons License Badge](http://i.creativecommons.org/l/by/3.0/88x31.png "Creative Commons Attribution") 35 | 36 | Screenshots 37 | ========================= 38 | 39 | ![Screenshot](https://github.com/grgcombs/MultiRowCalloutAnnotationView/raw/master/screenshot.png "Screenshot") 40 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/grgcombs/MultiRowCalloutAnnotationView/8c043825f3c034131ff0f2e44b1190d25d07d137/screenshot.png --------------------------------------------------------------------------------