├── .gitignore ├── PathHitTesting.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── PathHitTesting ├── AppDelegate.h ├── AppDelegate.m ├── DrawingView.h ├── DrawingView.m ├── PathHitTesting-Info.plist ├── PathHitTesting-Prefix.pch ├── Shape.h ├── Shape.m ├── ViewController.h ├── ViewController.m ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard └── main.m └── README.markdown /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | build/ 3 | *.pbxuser 4 | *.mode1v3 5 | xcuserdata/ 6 | -------------------------------------------------------------------------------- /PathHitTesting.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5D892D3014D6F72B005B3273 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D892D2F14D6F72B005B3273 /* UIKit.framework */; }; 11 | 5D892D3214D6F72B005B3273 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D892D3114D6F72B005B3273 /* Foundation.framework */; }; 12 | 5D892D3414D6F72B005B3273 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D892D3314D6F72B005B3273 /* CoreGraphics.framework */; }; 13 | 5D892D3A14D6F72B005B3273 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5D892D3814D6F72B005B3273 /* InfoPlist.strings */; }; 14 | 5D892D3C14D6F72C005B3273 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D892D3B14D6F72C005B3273 /* main.m */; }; 15 | 5D892D4014D6F72C005B3273 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D892D3F14D6F72C005B3273 /* AppDelegate.m */; }; 16 | 5D892D4314D6F72C005B3273 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D892D4114D6F72C005B3273 /* MainStoryboard.storyboard */; }; 17 | 5D892D4614D6F72C005B3273 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D892D4514D6F72C005B3273 /* ViewController.m */; }; 18 | 5D892D4E14D6F77E005B3273 /* DrawingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D892D4D14D6F77E005B3273 /* DrawingView.m */; }; 19 | 5D892D5414D70066005B3273 /* Shape.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D892D5314D70066005B3273 /* Shape.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 5D892D2B14D6F72B005B3273 /* PathHitTesting.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PathHitTesting.app; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 5D892D2F14D6F72B005B3273 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 25 | 5D892D3114D6F72B005B3273 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 26 | 5D892D3314D6F72B005B3273 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 27 | 5D892D3714D6F72B005B3273 /* PathHitTesting-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PathHitTesting-Info.plist"; sourceTree = ""; }; 28 | 5D892D3914D6F72B005B3273 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 29 | 5D892D3B14D6F72C005B3273 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 30 | 5D892D3D14D6F72C005B3273 /* PathHitTesting-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PathHitTesting-Prefix.pch"; sourceTree = ""; }; 31 | 5D892D3E14D6F72C005B3273 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 32 | 5D892D3F14D6F72C005B3273 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 33 | 5D892D4214D6F72C005B3273 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 34 | 5D892D4414D6F72C005B3273 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 35 | 5D892D4514D6F72C005B3273 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 36 | 5D892D4C14D6F77E005B3273 /* DrawingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DrawingView.h; sourceTree = ""; }; 37 | 5D892D4D14D6F77E005B3273 /* DrawingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DrawingView.m; sourceTree = ""; }; 38 | 5D892D5214D70066005B3273 /* Shape.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Shape.h; sourceTree = ""; }; 39 | 5D892D5314D70066005B3273 /* Shape.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Shape.m; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 5D892D2814D6F72B005B3273 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | 5D892D3014D6F72B005B3273 /* UIKit.framework in Frameworks */, 48 | 5D892D3214D6F72B005B3273 /* Foundation.framework in Frameworks */, 49 | 5D892D3414D6F72B005B3273 /* CoreGraphics.framework in Frameworks */, 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | 5D892D2014D6F72B005B3273 = { 57 | isa = PBXGroup; 58 | children = ( 59 | 5D892D3514D6F72B005B3273 /* PathHitTesting */, 60 | 5D892D2E14D6F72B005B3273 /* Frameworks */, 61 | 5D892D2C14D6F72B005B3273 /* Products */, 62 | ); 63 | sourceTree = ""; 64 | }; 65 | 5D892D2C14D6F72B005B3273 /* Products */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | 5D892D2B14D6F72B005B3273 /* PathHitTesting.app */, 69 | ); 70 | name = Products; 71 | sourceTree = ""; 72 | }; 73 | 5D892D2E14D6F72B005B3273 /* Frameworks */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 5D892D2F14D6F72B005B3273 /* UIKit.framework */, 77 | 5D892D3114D6F72B005B3273 /* Foundation.framework */, 78 | 5D892D3314D6F72B005B3273 /* CoreGraphics.framework */, 79 | ); 80 | name = Frameworks; 81 | sourceTree = ""; 82 | }; 83 | 5D892D3514D6F72B005B3273 /* PathHitTesting */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 5D892D3E14D6F72C005B3273 /* AppDelegate.h */, 87 | 5D892D3F14D6F72C005B3273 /* AppDelegate.m */, 88 | 5D892D4114D6F72C005B3273 /* MainStoryboard.storyboard */, 89 | 5D892D4414D6F72C005B3273 /* ViewController.h */, 90 | 5D892D4514D6F72C005B3273 /* ViewController.m */, 91 | 5D892D3614D6F72B005B3273 /* Supporting Files */, 92 | 5D892D4C14D6F77E005B3273 /* DrawingView.h */, 93 | 5D892D4D14D6F77E005B3273 /* DrawingView.m */, 94 | 5D892D5214D70066005B3273 /* Shape.h */, 95 | 5D892D5314D70066005B3273 /* Shape.m */, 96 | ); 97 | path = PathHitTesting; 98 | sourceTree = ""; 99 | }; 100 | 5D892D3614D6F72B005B3273 /* Supporting Files */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 5D892D3714D6F72B005B3273 /* PathHitTesting-Info.plist */, 104 | 5D892D3814D6F72B005B3273 /* InfoPlist.strings */, 105 | 5D892D3B14D6F72C005B3273 /* main.m */, 106 | 5D892D3D14D6F72C005B3273 /* PathHitTesting-Prefix.pch */, 107 | ); 108 | name = "Supporting Files"; 109 | sourceTree = ""; 110 | }; 111 | /* End PBXGroup section */ 112 | 113 | /* Begin PBXNativeTarget section */ 114 | 5D892D2A14D6F72B005B3273 /* PathHitTesting */ = { 115 | isa = PBXNativeTarget; 116 | buildConfigurationList = 5D892D4914D6F72C005B3273 /* Build configuration list for PBXNativeTarget "PathHitTesting" */; 117 | buildPhases = ( 118 | 5D892D2714D6F72B005B3273 /* Sources */, 119 | 5D892D2814D6F72B005B3273 /* Frameworks */, 120 | 5D892D2914D6F72B005B3273 /* Resources */, 121 | ); 122 | buildRules = ( 123 | ); 124 | dependencies = ( 125 | ); 126 | name = PathHitTesting; 127 | productName = PathHitTesting; 128 | productReference = 5D892D2B14D6F72B005B3273 /* PathHitTesting.app */; 129 | productType = "com.apple.product-type.application"; 130 | }; 131 | /* End PBXNativeTarget section */ 132 | 133 | /* Begin PBXProject section */ 134 | 5D892D2214D6F72B005B3273 /* Project object */ = { 135 | isa = PBXProject; 136 | attributes = { 137 | LastUpgradeCheck = 0420; 138 | ORGANIZATIONNAME = "Ole Begemann"; 139 | }; 140 | buildConfigurationList = 5D892D2514D6F72B005B3273 /* Build configuration list for PBXProject "PathHitTesting" */; 141 | compatibilityVersion = "Xcode 3.2"; 142 | developmentRegion = English; 143 | hasScannedForEncodings = 0; 144 | knownRegions = ( 145 | en, 146 | ); 147 | mainGroup = 5D892D2014D6F72B005B3273; 148 | productRefGroup = 5D892D2C14D6F72B005B3273 /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 5D892D2A14D6F72B005B3273 /* PathHitTesting */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 5D892D2914D6F72B005B3273 /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 5D892D3A14D6F72B005B3273 /* InfoPlist.strings in Resources */, 163 | 5D892D4314D6F72C005B3273 /* MainStoryboard.storyboard in Resources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXResourcesBuildPhase section */ 168 | 169 | /* Begin PBXSourcesBuildPhase section */ 170 | 5D892D2714D6F72B005B3273 /* Sources */ = { 171 | isa = PBXSourcesBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 5D892D3C14D6F72C005B3273 /* main.m in Sources */, 175 | 5D892D4014D6F72C005B3273 /* AppDelegate.m in Sources */, 176 | 5D892D4614D6F72C005B3273 /* ViewController.m in Sources */, 177 | 5D892D4E14D6F77E005B3273 /* DrawingView.m in Sources */, 178 | 5D892D5414D70066005B3273 /* Shape.m in Sources */, 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | }; 182 | /* End PBXSourcesBuildPhase section */ 183 | 184 | /* Begin PBXVariantGroup section */ 185 | 5D892D3814D6F72B005B3273 /* InfoPlist.strings */ = { 186 | isa = PBXVariantGroup; 187 | children = ( 188 | 5D892D3914D6F72B005B3273 /* en */, 189 | ); 190 | name = InfoPlist.strings; 191 | sourceTree = ""; 192 | }; 193 | 5D892D4114D6F72C005B3273 /* MainStoryboard.storyboard */ = { 194 | isa = PBXVariantGroup; 195 | children = ( 196 | 5D892D4214D6F72C005B3273 /* en */, 197 | ); 198 | name = MainStoryboard.storyboard; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXVariantGroup section */ 202 | 203 | /* Begin XCBuildConfiguration section */ 204 | 5D892D4714D6F72C005B3273 /* Debug */ = { 205 | isa = XCBuildConfiguration; 206 | buildSettings = { 207 | ALWAYS_SEARCH_USER_PATHS = NO; 208 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 209 | CLANG_ENABLE_OBJC_ARC = YES; 210 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 211 | COPY_PHASE_STRIP = NO; 212 | GCC_C_LANGUAGE_STANDARD = gnu99; 213 | GCC_DYNAMIC_NO_PIC = NO; 214 | GCC_OPTIMIZATION_LEVEL = 0; 215 | GCC_PREPROCESSOR_DEFINITIONS = ( 216 | "DEBUG=1", 217 | "$(inherited)", 218 | ); 219 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 220 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 221 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 222 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 223 | GCC_WARN_UNUSED_VARIABLE = YES; 224 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 225 | SDKROOT = iphoneos; 226 | TARGETED_DEVICE_FAMILY = 2; 227 | }; 228 | name = Debug; 229 | }; 230 | 5D892D4814D6F72C005B3273 /* Release */ = { 231 | isa = XCBuildConfiguration; 232 | buildSettings = { 233 | ALWAYS_SEARCH_USER_PATHS = NO; 234 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 235 | CLANG_ENABLE_OBJC_ARC = YES; 236 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 237 | COPY_PHASE_STRIP = YES; 238 | GCC_C_LANGUAGE_STANDARD = gnu99; 239 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 240 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 241 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 242 | GCC_WARN_UNUSED_VARIABLE = YES; 243 | IPHONEOS_DEPLOYMENT_TARGET = 5.0; 244 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 245 | SDKROOT = iphoneos; 246 | TARGETED_DEVICE_FAMILY = 2; 247 | VALIDATE_PRODUCT = YES; 248 | }; 249 | name = Release; 250 | }; 251 | 5D892D4A14D6F72C005B3273 /* Debug */ = { 252 | isa = XCBuildConfiguration; 253 | buildSettings = { 254 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 255 | GCC_PREFIX_HEADER = "PathHitTesting/PathHitTesting-Prefix.pch"; 256 | INFOPLIST_FILE = "PathHitTesting/PathHitTesting-Info.plist"; 257 | PRODUCT_NAME = "$(TARGET_NAME)"; 258 | WRAPPER_EXTENSION = app; 259 | }; 260 | name = Debug; 261 | }; 262 | 5D892D4B14D6F72C005B3273 /* Release */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 266 | GCC_PREFIX_HEADER = "PathHitTesting/PathHitTesting-Prefix.pch"; 267 | INFOPLIST_FILE = "PathHitTesting/PathHitTesting-Info.plist"; 268 | PRODUCT_NAME = "$(TARGET_NAME)"; 269 | WRAPPER_EXTENSION = app; 270 | }; 271 | name = Release; 272 | }; 273 | /* End XCBuildConfiguration section */ 274 | 275 | /* Begin XCConfigurationList section */ 276 | 5D892D2514D6F72B005B3273 /* Build configuration list for PBXProject "PathHitTesting" */ = { 277 | isa = XCConfigurationList; 278 | buildConfigurations = ( 279 | 5D892D4714D6F72C005B3273 /* Debug */, 280 | 5D892D4814D6F72C005B3273 /* Release */, 281 | ); 282 | defaultConfigurationIsVisible = 0; 283 | defaultConfigurationName = Release; 284 | }; 285 | 5D892D4914D6F72C005B3273 /* Build configuration list for PBXNativeTarget "PathHitTesting" */ = { 286 | isa = XCConfigurationList; 287 | buildConfigurations = ( 288 | 5D892D4A14D6F72C005B3273 /* Debug */, 289 | 5D892D4B14D6F72C005B3273 /* Release */, 290 | ); 291 | defaultConfigurationIsVisible = 0; 292 | }; 293 | /* End XCConfigurationList section */ 294 | }; 295 | rootObject = 5D892D2214D6F72B005B3273 /* Project object */; 296 | } 297 | -------------------------------------------------------------------------------- /PathHitTesting.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PathHitTesting/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /PathHitTesting/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | @synthesize window = _window; 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | // Override point for customization after application launch. 18 | return YES; 19 | } 20 | 21 | - (void)applicationWillResignActive:(UIApplication *)application 22 | { 23 | /* 24 | Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | */ 27 | } 28 | 29 | - (void)applicationDidEnterBackground:(UIApplication *)application 30 | { 31 | /* 32 | Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | */ 35 | } 36 | 37 | - (void)applicationWillEnterForeground:(UIApplication *)application 38 | { 39 | /* 40 | Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 41 | */ 42 | } 43 | 44 | - (void)applicationDidBecomeActive:(UIApplication *)application 45 | { 46 | /* 47 | Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 48 | */ 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application 52 | { 53 | /* 54 | Called when the application is about to terminate. 55 | Save data if appropriate. 56 | See also applicationDidEnterBackground:. 57 | */ 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /PathHitTesting/DrawingView.h: -------------------------------------------------------------------------------- 1 | // 2 | // DrawingView.h 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol DrawingViewDataSource; 12 | 13 | @interface DrawingView : UIView 14 | 15 | @property (nonatomic, weak) IBOutlet id dataSource; 16 | 17 | - (void)reloadData; 18 | - (void)reloadDataInRect:(CGRect)rect; 19 | 20 | @end 21 | 22 | 23 | @protocol DrawingViewDataSource 24 | 25 | @required 26 | - (NSUInteger)numberOfShapesInDrawingView:(DrawingView *)drawingView; 27 | - (UIBezierPath *)drawingView:(DrawingView *)drawingView pathForShapeAtIndex:(NSUInteger)shapeIndex; 28 | - (UIColor *)drawingView:(DrawingView *)drawingView lineColorForShapeAtIndex:(NSUInteger)shapeIndex; 29 | 30 | @optional 31 | - (NSUInteger)indexOfSelectedShapeInDrawingView:(DrawingView *)drawingView; 32 | 33 | @end -------------------------------------------------------------------------------- /PathHitTesting/DrawingView.m: -------------------------------------------------------------------------------- 1 | // 2 | // DrawingView.m 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import "DrawingView.h" 10 | #import "Shape.h" 11 | 12 | 13 | @implementation DrawingView 14 | 15 | @synthesize dataSource = _dataSource; 16 | 17 | - (void)reloadData 18 | { 19 | [self setNeedsDisplay]; 20 | } 21 | 22 | - (void)reloadDataInRect:(CGRect)rect 23 | { 24 | [self setNeedsDisplayInRect:rect]; 25 | } 26 | 27 | - (void)drawRect:(CGRect)rect 28 | { 29 | NSUInteger numberOfShapes = [self.dataSource numberOfShapesInDrawingView:self]; 30 | NSUInteger indexOfSelectedShape = NSNotFound; 31 | if ([self.dataSource respondsToSelector:@selector(indexOfSelectedShapeInDrawingView:)]) { 32 | indexOfSelectedShape = [self.dataSource indexOfSelectedShapeInDrawingView:self]; 33 | } 34 | 35 | for (NSUInteger shapeIndex = 0; shapeIndex < numberOfShapes; shapeIndex++) 36 | { 37 | UIBezierPath *path = [self.dataSource drawingView:self pathForShapeAtIndex:shapeIndex]; 38 | if (CGRectIntersectsRect(rect, CGRectInset(path.bounds, -(path.lineWidth + 1.0f), -(path.lineWidth + 1.0f)))) 39 | { 40 | UIColor *lineColor = [self.dataSource drawingView:self lineColorForShapeAtIndex:shapeIndex]; 41 | [lineColor setStroke]; 42 | [path stroke]; 43 | 44 | if (shapeIndex == indexOfSelectedShape) { 45 | UIBezierPath *pathCopy = [path copy]; 46 | CGPathRef cgPathSelectionRect = CGPathCreateCopyByStrokingPath(pathCopy.CGPath, NULL, pathCopy.lineWidth, pathCopy.lineCapStyle, pathCopy.lineJoinStyle, pathCopy.miterLimit); 47 | UIBezierPath *selectionRect = [UIBezierPath bezierPathWithCGPath:cgPathSelectionRect]; 48 | CGPathRelease(cgPathSelectionRect); 49 | 50 | CGFloat dashStyle[] = { 5.0f, 5.0f }; 51 | [selectionRect setLineDash:dashStyle count:2 phase:0]; 52 | [[UIColor blackColor] setStroke]; 53 | [selectionRect stroke]; 54 | } 55 | } 56 | } 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /PathHitTesting/PathHitTesting-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.olebegemann.${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 | UIMainStoryboardFile 30 | MainStoryboard 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /PathHitTesting/PathHitTesting-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'PathHitTesting' target in the 'PathHitTesting' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /PathHitTesting/Shape.h: -------------------------------------------------------------------------------- 1 | // 2 | // Shape.h 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum { 12 | ShapeTypeRect, 13 | ShapeTypeEllipse, 14 | ShapeTypeHouse, 15 | ShapeTypeArc, 16 | SHAPE_TYPE_COUNT 17 | } ShapeType; 18 | 19 | 20 | @interface Shape : NSObject 21 | 22 | + (id)randomShapeInBounds:(CGRect)maxBounds; 23 | + (id)shapeWithPath:(UIBezierPath *)path lineColor:(UIColor *)lineColor; 24 | - (id)initWithPath:(UIBezierPath *)path lineColor:(UIColor *)lineColor; 25 | 26 | - (BOOL)containsPoint:(CGPoint)point; 27 | - (void)moveBy:(CGPoint)delta; 28 | 29 | @property (nonatomic, strong) UIBezierPath *path; 30 | @property (nonatomic, strong) UIColor *lineColor; 31 | @property (nonatomic, assign, readonly) CGRect totalBounds; 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /PathHitTesting/Shape.m: -------------------------------------------------------------------------------- 1 | // 2 | // Shape.m 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import "Shape.h" 10 | 11 | @interface Shape () 12 | 13 | @property (nonatomic, strong) UIBezierPath *tapTarget; 14 | 15 | - (UIBezierPath *)tapTargetForPath:(UIBezierPath *)path; 16 | 17 | + (ShapeType)randomShapeType; 18 | + (CGRect)randomRectInBounds:(CGRect)maxBounds; 19 | + (UIColor *)randomColor; 20 | + (CGFloat)randomLineWidth; 21 | + (UIBezierPath *)houseInRect:(CGRect)bounds; 22 | + (UIBezierPath *)arcInRect:(CGRect)bounds; 23 | 24 | @end 25 | 26 | 27 | @implementation Shape 28 | 29 | @synthesize path = _path; 30 | @synthesize lineColor = _lineColor; 31 | @synthesize tapTarget = _tapTarget; 32 | @dynamic totalBounds; 33 | 34 | 35 | + (id)randomShapeInBounds:(CGRect)maxBounds 36 | { 37 | UIBezierPath *path = nil; 38 | CGRect bounds = [self randomRectInBounds:maxBounds]; 39 | ShapeType type = [self randomShapeType]; 40 | switch (type) { 41 | case ShapeTypeRect: 42 | path = [UIBezierPath bezierPathWithRect:bounds]; 43 | break; 44 | case ShapeTypeEllipse: 45 | path = [UIBezierPath bezierPathWithOvalInRect:bounds]; 46 | break; 47 | case ShapeTypeHouse: 48 | path = [self houseInRect:bounds]; 49 | break; 50 | case ShapeTypeArc: 51 | path = [self arcInRect:bounds]; 52 | break; 53 | default: 54 | path = [UIBezierPath bezierPathWithRect:bounds]; 55 | break; 56 | } 57 | 58 | path.lineWidth = [self randomLineWidth]; 59 | UIColor *lineColor = [self randomColor]; 60 | 61 | return [[self alloc] initWithPath:path lineColor:lineColor]; 62 | } 63 | 64 | + (id)shapeWithPath:(UIBezierPath *)path lineColor:(UIColor *)lineColor 65 | { 66 | return [[self alloc] initWithPath:path lineColor:lineColor]; 67 | } 68 | 69 | - (id)initWithPath:(UIBezierPath *)path lineColor:(UIColor *)lineColor 70 | { 71 | self = [super init]; 72 | if (self != nil) { 73 | _path = path; 74 | _lineColor = lineColor; 75 | _tapTarget = [self tapTargetForPath:_path]; 76 | } 77 | return self; 78 | } 79 | 80 | - (id)init 81 | { 82 | UIBezierPath *defaultPath = [UIBezierPath bezierPathWithRect:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)]; 83 | UIColor *defaultLineColor = [UIColor blackColor]; 84 | return [self initWithPath:defaultPath lineColor:defaultLineColor]; 85 | } 86 | 87 | 88 | #pragma mark - Description 89 | 90 | - (NSString *)description 91 | { 92 | return [NSString stringWithFormat:@"", self, NSStringFromCGRect(self.path.bounds), self.lineColor]; 93 | } 94 | 95 | 96 | #pragma mark - Hit Testing 97 | 98 | - (UIBezierPath *)tapTargetForPath:(UIBezierPath *)path 99 | { 100 | if (path == nil) { 101 | return nil; 102 | } 103 | 104 | CGPathRef tapTargetPath = CGPathCreateCopyByStrokingPath(path.CGPath, NULL, fmaxf(35.0f, path.lineWidth), path.lineCapStyle, path.lineJoinStyle, path.miterLimit); 105 | 106 | if (tapTargetPath == NULL) { 107 | return nil; 108 | } 109 | 110 | UIBezierPath *tapTarget = [UIBezierPath bezierPathWithCGPath:tapTargetPath]; 111 | CGPathRelease(tapTargetPath); 112 | return tapTarget; 113 | } 114 | 115 | - (BOOL)containsPoint:(CGPoint)point 116 | { 117 | return [self.tapTarget containsPoint:point]; 118 | } 119 | 120 | 121 | #pragma mark - Bounds 122 | 123 | - (CGRect)totalBounds 124 | { 125 | if (self.path == nil) { 126 | return CGRectZero; 127 | } 128 | 129 | return CGRectInset(self.path.bounds, -(self.path.lineWidth + 1.0f), -(self.path.lineWidth + 1.0f)); 130 | } 131 | 132 | 133 | #pragma mark - Modifying Shapes 134 | 135 | - (void)moveBy:(CGPoint)delta 136 | { 137 | CGAffineTransform transform = CGAffineTransformMakeTranslation(delta.x, delta.y); 138 | [self.path applyTransform:transform]; 139 | [self.tapTarget applyTransform:transform]; 140 | } 141 | 142 | 143 | 144 | #pragma mark - Random Shape Generator Methods 145 | 146 | + (ShapeType)randomShapeType 147 | { 148 | return arc4random_uniform(SHAPE_TYPE_COUNT); 149 | } 150 | 151 | + (CGRect)randomRectInBounds:(CGRect)maxBounds 152 | { 153 | CGRect normalizedBounds = CGRectStandardize(maxBounds); 154 | uint32_t minOriginX = normalizedBounds.origin.x; 155 | uint32_t minOriginY = normalizedBounds.origin.y; 156 | uint32_t minWidth = 44; 157 | uint32_t minHeight = 44; 158 | 159 | uint32_t maxOriginX = normalizedBounds.size.width - minWidth; 160 | uint32_t maxOriginY = normalizedBounds.size.height - minHeight; 161 | 162 | uint32_t originX = arc4random_uniform(maxOriginX - minOriginX) + minOriginX; 163 | uint32_t originY = arc4random_uniform(maxOriginY - minOriginY) + minOriginY; 164 | 165 | uint32_t maxWidth = normalizedBounds.size.width - originX; 166 | uint32_t maxHeight = normalizedBounds.size.height - originY; 167 | 168 | uint32_t width = arc4random_uniform(maxWidth - minWidth) + minWidth; 169 | uint32_t height = arc4random_uniform(maxHeight - minHeight) + minHeight; 170 | 171 | CGRect randomRect = CGRectMake(originX, originY, width, height); 172 | return randomRect; 173 | } 174 | 175 | + (UIColor *)randomColor 176 | { 177 | NSArray *colors = [NSArray arrayWithObjects: 178 | [UIColor blueColor], 179 | [UIColor redColor], 180 | [UIColor greenColor], 181 | [UIColor yellowColor], 182 | [UIColor magentaColor], 183 | [UIColor brownColor], 184 | [UIColor purpleColor], 185 | [UIColor orangeColor], 186 | nil]; 187 | uint32_t colorIndex = arc4random_uniform([colors count]); 188 | return [colors objectAtIndex:colorIndex]; 189 | } 190 | 191 | + (CGFloat)randomLineWidth 192 | { 193 | uint32_t maxLineWidth = 15; 194 | CGFloat lineWidth = arc4random_uniform(maxLineWidth) + 1.0f; // avoid lineWidth == 0.0f 195 | return lineWidth; 196 | } 197 | 198 | + (UIBezierPath *)houseInRect:(CGRect)bounds 199 | { 200 | CGPoint bottomLeft = CGPointMake(CGRectGetMinX(bounds), CGRectGetMinY(bounds)); 201 | CGPoint topLeft = CGPointMake(CGRectGetMinX(bounds), CGRectGetMinY(bounds) + CGRectGetHeight(bounds) * 2.0f/3.0f); 202 | CGPoint bottomRight = CGPointMake(CGRectGetMaxX(bounds), CGRectGetMinY(bounds)); 203 | CGPoint topRight = CGPointMake(CGRectGetMaxX(bounds), CGRectGetMinY(bounds) + CGRectGetHeight(bounds) * 2.0f/3.0f); 204 | CGPoint roofTip = CGPointMake(CGRectGetMidX(bounds), CGRectGetMaxY(bounds)); 205 | 206 | UIBezierPath *path = [UIBezierPath bezierPath]; 207 | [path moveToPoint:bottomLeft]; 208 | [path addLineToPoint:topLeft]; 209 | [path addLineToPoint:roofTip]; 210 | [path addLineToPoint:topRight]; 211 | [path addLineToPoint:topLeft]; 212 | [path addLineToPoint:bottomRight]; 213 | [path addLineToPoint:topRight]; 214 | [path addLineToPoint:bottomLeft]; 215 | [path addLineToPoint:bottomRight]; 216 | 217 | path.lineJoinStyle = kCGLineJoinRound; 218 | 219 | CGAffineTransform transform = CGAffineTransformIdentity; 220 | transform = CGAffineTransformTranslate(transform, bounds.origin.x, bounds.origin.y); 221 | transform = CGAffineTransformTranslate(transform, 0.0, bounds.size.height); 222 | transform = CGAffineTransformScale(transform, 1.0f, -1.0f); 223 | transform = CGAffineTransformTranslate(transform, -bounds.origin.x, -bounds.origin.y); 224 | [path applyTransform:transform]; 225 | 226 | return path; 227 | } 228 | 229 | + (UIBezierPath *)arcInRect:(CGRect)bounds 230 | { 231 | CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); 232 | CGPoint centerRight = CGPointMake(CGRectGetMaxX(bounds), CGRectGetMidY(bounds)); 233 | CGFloat radius = CGRectGetWidth(bounds) / 2.0f; 234 | CGPoint center2 = CGPointMake(center.x, center.y - radius / 2.0f); 235 | 236 | UIBezierPath *path = [UIBezierPath bezierPath]; 237 | [path moveToPoint:centerRight]; 238 | [path addArcWithCenter:center radius:radius startAngle:0.0f endAngle:M_PI * 1.5f clockwise:YES]; 239 | [path addArcWithCenter:center2 radius:radius / 2.0f startAngle:M_PI * 1.5f endAngle:M_PI clockwise:YES]; 240 | 241 | path.lineJoinStyle = kCGLineJoinRound; 242 | 243 | CGAffineTransform transform = CGAffineTransformIdentity; 244 | transform = CGAffineTransformTranslate(transform, bounds.origin.x, bounds.origin.y); 245 | transform = CGAffineTransformTranslate(transform, 0.0, bounds.size.height); 246 | transform = CGAffineTransformScale(transform, 1.0f, -1.0f); 247 | transform = CGAffineTransformTranslate(transform, -bounds.origin.x, -bounds.origin.y); 248 | [path applyTransform:transform]; 249 | 250 | return path; 251 | } 252 | 253 | @end 254 | -------------------------------------------------------------------------------- /PathHitTesting/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DrawingView.h" 11 | 12 | @interface ViewController : UIViewController 13 | 14 | @property (weak, nonatomic) IBOutlet DrawingView *drawingView; 15 | @property (weak, nonatomic) IBOutlet UIBarButtonItem *deleteShapeButton; 16 | 17 | - (IBAction)addButtonTapped:(id)sender; 18 | - (IBAction)deleteButtonTapped:(id)sender; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /PathHitTesting/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Shape.h" 11 | 12 | 13 | @interface ViewController () 14 | 15 | @property (nonatomic, strong) NSMutableArray *shapes; 16 | @property (nonatomic, assign) NSUInteger selectedShapeIndex; 17 | @property (nonatomic, readonly) Shape *selectedShape; 18 | 19 | - (void)addShape:(Shape *)newShape; 20 | - (NSUInteger)hitTest:(CGPoint)point; 21 | 22 | @end 23 | 24 | 25 | 26 | @implementation ViewController 27 | 28 | @synthesize drawingView = _drawingView; 29 | @synthesize deleteShapeButton = _deleteShapeButton; 30 | @synthesize shapes = _shapes; 31 | @synthesize selectedShapeIndex = _selectedShapeIndex; 32 | @dynamic selectedShape; 33 | 34 | 35 | - (void)didReceiveMemoryWarning 36 | { 37 | [super didReceiveMemoryWarning]; 38 | // Release any cached data, images, etc that aren't in use. 39 | } 40 | 41 | #pragma mark - View lifecycle 42 | 43 | - (void)viewDidLoad 44 | { 45 | [super viewDidLoad]; 46 | // Do any additional setup after loading the view, typically from a nib. 47 | 48 | _selectedShapeIndex = NSNotFound; 49 | self.shapes = [NSMutableArray array]; 50 | 51 | UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected:)]; 52 | [self.drawingView addGestureRecognizer:tapRecognizer]; 53 | 54 | UIPanGestureRecognizer *panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panDetected:)]; 55 | [self.drawingView addGestureRecognizer:panRecognizer]; 56 | 57 | [self.drawingView reloadData]; 58 | } 59 | 60 | - (void)viewDidUnload 61 | { 62 | [self setDrawingView:nil]; 63 | [self setDeleteShapeButton:nil]; 64 | [super viewDidUnload]; 65 | // Release any retained subviews of the main view. 66 | // e.g. self.myOutlet = nil; 67 | } 68 | 69 | - (void)viewWillAppear:(BOOL)animated 70 | { 71 | [super viewWillAppear:animated]; 72 | } 73 | 74 | - (void)viewDidAppear:(BOOL)animated 75 | { 76 | [super viewDidAppear:animated]; 77 | } 78 | 79 | - (void)viewWillDisappear:(BOOL)animated 80 | { 81 | [super viewWillDisappear:animated]; 82 | } 83 | 84 | - (void)viewDidDisappear:(BOOL)animated 85 | { 86 | [super viewDidDisappear:animated]; 87 | } 88 | 89 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 90 | { 91 | // Return YES for supported orientations 92 | return YES; 93 | } 94 | 95 | 96 | #pragma mark - Shape management 97 | 98 | - (IBAction)addButtonTapped:(id)sender 99 | { 100 | CGRect maxBounds = CGRectInset(self.drawingView.bounds, 10.0f, 10.0f); 101 | Shape *newShape = [Shape randomShapeInBounds:maxBounds]; 102 | [self addShape:newShape]; 103 | } 104 | 105 | - (void)addShape:(Shape *)newShape 106 | { 107 | [self.shapes addObject:newShape]; 108 | [self.drawingView reloadDataInRect:newShape.totalBounds]; 109 | } 110 | 111 | - (IBAction)deleteButtonTapped:(id)sender 112 | { 113 | if (self.selectedShapeIndex == NSNotFound) { 114 | return; 115 | } 116 | 117 | CGRect rectToRedraw = self.selectedShape.totalBounds; 118 | [self.shapes removeObjectAtIndex:self.selectedShapeIndex]; 119 | self.selectedShapeIndex = NSNotFound; 120 | [self.drawingView reloadDataInRect:rectToRedraw]; 121 | } 122 | 123 | - (void)setSelectedShapeIndex:(NSUInteger)selectedShapeIndex 124 | { 125 | CGRect oldSelectionBounds = CGRectZero; 126 | if (_selectedShapeIndex < [self.shapes count]) { 127 | oldSelectionBounds = self.selectedShape.totalBounds; 128 | } 129 | _selectedShapeIndex = selectedShapeIndex; 130 | CGRect newSelectionBounds = self.selectedShape.totalBounds; 131 | CGRect rectToRedraw = CGRectUnion(oldSelectionBounds, newSelectionBounds); 132 | [_drawingView setNeedsDisplayInRect:rectToRedraw]; 133 | self.deleteShapeButton.enabled = (_selectedShapeIndex != NSNotFound); 134 | } 135 | 136 | - (Shape *)selectedShape 137 | { 138 | if (_selectedShapeIndex == NSNotFound) { 139 | return nil; 140 | } 141 | return [self.shapes objectAtIndex:_selectedShapeIndex]; 142 | } 143 | 144 | 145 | #pragma mark - Hit Testing 146 | 147 | - (NSUInteger)hitTest:(CGPoint)point 148 | { 149 | __block NSUInteger hitShapeIndex = NSNotFound; 150 | [self.shapes enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id shape, NSUInteger idx, BOOL *stop) { 151 | if ([shape containsPoint:point]) { 152 | hitShapeIndex = idx; 153 | *stop = YES; 154 | } 155 | }]; 156 | return hitShapeIndex; 157 | } 158 | 159 | 160 | #pragma mark - Touch handling 161 | 162 | - (void)tapDetected:(UITapGestureRecognizer *)tapRecognizer 163 | { 164 | CGPoint tapLocation = [tapRecognizer locationInView:self.drawingView]; 165 | self.selectedShapeIndex = [self hitTest:tapLocation]; 166 | } 167 | 168 | - (void)panDetected:(UIPanGestureRecognizer *)panRecognizer 169 | { 170 | switch (panRecognizer.state) { 171 | case UIGestureRecognizerStateBegan: { 172 | CGPoint tapLocation = [panRecognizer locationInView:self.drawingView]; 173 | self.selectedShapeIndex = [self hitTest:tapLocation]; 174 | break; 175 | } 176 | case UIGestureRecognizerStateChanged: { 177 | CGPoint translation = [panRecognizer translationInView:self.drawingView]; 178 | CGRect originalBounds = self.selectedShape.totalBounds; 179 | CGRect newBounds = CGRectApplyAffineTransform(originalBounds, CGAffineTransformMakeTranslation(translation.x, translation.y)); 180 | CGRect rectToRedraw = CGRectUnion(originalBounds, newBounds); 181 | 182 | [self.selectedShape moveBy:translation]; 183 | [self.drawingView reloadDataInRect:rectToRedraw]; 184 | [panRecognizer setTranslation:CGPointZero inView:self.drawingView]; 185 | } 186 | default: 187 | break; 188 | } 189 | } 190 | 191 | 192 | #pragma mark - DrawingViewDataSource 193 | 194 | - (NSUInteger)numberOfShapesInDrawingView:(DrawingView *)drawingView 195 | { 196 | return [self.shapes count]; 197 | } 198 | 199 | - (UIBezierPath *)drawingView:(DrawingView *)drawingView pathForShapeAtIndex:(NSUInteger)shapeIndex 200 | { 201 | Shape *shape = [self.shapes objectAtIndex:shapeIndex]; 202 | return shape.path; 203 | } 204 | 205 | - (UIColor *)drawingView:(DrawingView *)drawingView lineColorForShapeAtIndex:(NSUInteger)shapeIndex 206 | { 207 | Shape *shape = [self.shapes objectAtIndex:shapeIndex]; 208 | return shape.lineColor; 209 | } 210 | 211 | - (NSUInteger)indexOfSelectedShapeInDrawingView:(DrawingView *)drawingView 212 | { 213 | return self.selectedShapeIndex; 214 | } 215 | 216 | @end -------------------------------------------------------------------------------- /PathHitTesting/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PathHitTesting/en.lproj/MainStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /PathHitTesting/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PathHitTesting 4 | // 5 | // Created by Ole Begemann on 30.01.12. 6 | // Copyright (c) 2012 Ole Begemann. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | This is a demo iOS app I developed to illustrate [a blog post on hit testing](http://oleb.net/blog/2012/02/cgpath-hit-testing/) with the `CGPathCreateCopyByStrokingPath()` function. Please see the article on my blog for more info. --------------------------------------------------------------------------------