├── Images └── InterfaceBuilderSubclassExample.png ├── Tests ├── KGHitTestingViewsTests.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj └── KGHitTestingViewsTests │ ├── Info.plist │ ├── KGHitTestBaseTest.h │ ├── KGHitTestViewTests.m │ ├── KGHitTestButtonTests.m │ ├── KGHitTestControlTests.m │ ├── KGHitTestBaseTest.m │ └── KGHitTestCategoryTests.m ├── Example ├── KGHitTestingViewsExample.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj └── KGHitTestingViewsExample │ ├── ViewController.h │ ├── AppDelegate.h │ ├── main.m │ ├── AppDelegate.m │ ├── Images.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── LaunchImage.launchimage │ │ └── Contents.json │ ├── Info.plist │ ├── Base.lproj │ ├── LaunchScreen.xib │ └── Main.storyboard │ └── ViewController.m ├── .gitignore ├── KGHitTestingViews.podspec ├── LICENSE ├── KGHitTestingViews ├── UIView+KGHitTesting.h ├── KGHitTestingHelper.h ├── KGHitTestingView.m ├── KGHitTestingButton.m ├── KGHitTestingControl.m ├── KGHitTesting.h ├── KGHitTestingHelper.m ├── KGHitTestingView.h ├── KGHitTestingButton.h ├── KGHitTestingControl.h └── UIView+KGHitTesting.m └── README.md /Images/InterfaceBuilderSubclassExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kgaidis/KGHitTestingViews/HEAD/Images/InterfaceBuilderSubclassExample.png -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pbxuser 3 | !default.pbxuser 4 | *.mode1v3 5 | !default.mode1v3 6 | *.mode2v3 7 | !default.mode2v3 8 | *.perspectivev3 9 | !default.perspectivev3 10 | xcuserdata 11 | *.xccheckout 12 | *.moved-aside 13 | DerivedData 14 | *.hmap 15 | *.ipa 16 | *.xcuserstate 17 | .DS_Store -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // Copyright (c) 2015 Krisjanis Gaidis. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | @end -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // Copyright (c) 2015 Krisjanis Gaidis. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // Copyright (c) 2015 Krisjanis Gaidis. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // Copyright (c) 2015 Krisjanis Gaidis. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /KGHitTestingViews.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'KGHitTestingViews' 3 | s.version = '0.9.5' 4 | s.license = { :type => "MIT", :file => "LICENSE" } 5 | s.summary = 'A small helper library to effortlessly increase the hit test area of a view, control or a button.' 6 | s.author = { 'Krisjanis Gaidis' => 'http://www.krisjanisgaidis.com/' } 7 | s.source = { :git => 'https://github.com/kgaidis/KGHitTestingViews.git', :tag => '0.9.5' } 8 | s.homepage = 'http://github.com/kgaidis/KGHitTestingViews' 9 | s.source_files = 'KGHitTestingViews' 10 | s.platform = :ios, "6.0" 11 | s.requires_arc = true 12 | 13 | end -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.kg.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "minimum-system-version" : "7.0", 7 | "subtype" : "retina4", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "idiom" : "iphone", 12 | "scale" : "1x", 13 | "orientation" : "portrait" 14 | }, 15 | { 16 | "idiom" : "iphone", 17 | "scale" : "2x", 18 | "orientation" : "portrait" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "iphone", 23 | "subtype" : "retina4", 24 | "scale" : "2x" 25 | }, 26 | { 27 | "orientation" : "portrait", 28 | "idiom" : "iphone", 29 | "minimum-system-version" : "7.0", 30 | "scale" : "2x" 31 | } 32 | ], 33 | "info" : { 34 | "version" : 1, 35 | "author" : "xcode" 36 | } 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This code is distributed under the terms and conditions of the MIT license. 2 | 3 | Copyright (c) 2014-2015 Krisjanis Gaidis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/KGHitTestBaseTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestBaseTest.h 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 5/10/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import "KGHitTesting.h" 13 | 14 | static const CGFloat kWidth = 10.0; 15 | static const CGFloat kHeight = 10.0; 16 | static const CGFloat kMinimumHitTestWidth = 20.0; 17 | static const CGFloat kMinimumHitTestHeight = 20.0; 18 | 19 | static const CGFloat kWidthOffset = (kMinimumHitTestWidth - kWidth) / 2; 20 | static const CGFloat kHeightOffset = (kMinimumHitTestHeight - kHeight) / 2; 21 | 22 | @interface KGHitTestBaseTest : XCTestCase 23 | 24 | @property (strong, nonatomic) id hitTestObject; 25 | 26 | #pragma mark - Top left corner - 27 | - (void)_testTopLeftCornerInBounds; 28 | - (void)_testTopLeftCornerOutOfBounds; 29 | 30 | #pragma mark - Top right corner - 31 | - (void)_testTopRightCornerInBounds; 32 | - (void)_testTopRightCornerOutOfBounds; 33 | 34 | #pragma mark - Bottom right corner - 35 | - (void)_testBottomRightCornerInBounds; 36 | - (void)_testBottomRightCornerOutOfBounds; 37 | 38 | #pragma mark - Bottom left corner - 39 | - (void)_testBottomLeftCornerInBounds; 40 | - (void)_testBottomLeftCornerOutOfBounds; 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.kg.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/KGHitTestViewTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestViewTests.m 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import "KGHitTestingView.h" 12 | #import "KGHitTestBaseTest.h" 13 | 14 | @interface KGHitTestViewTests : KGHitTestBaseTest 15 | 16 | @end 17 | 18 | @implementation KGHitTestViewTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | // Put setup code here. This method is called before the invocation of each test method in the class. 23 | self.hitTestObject = [[KGHitTestingView alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)]; 24 | } 25 | 26 | #pragma mark - Top left corner - 27 | - (void)testTopLeftCornerInBounds { [self _testTopLeftCornerInBounds]; } 28 | - (void)testTopLeftCornerOutOfBounds { [self _testTopLeftCornerOutOfBounds]; } 29 | 30 | #pragma mark - Top right corner - 31 | - (void)testTopRightCornerInBounds { [self _testTopRightCornerInBounds]; } 32 | - (void)testTopRightCornerOutOfBounds { [self _testTopRightCornerOutOfBounds]; } 33 | 34 | #pragma mark - Bottom right corner - 35 | - (void)testBottomRightCornerInBounds { [self _testBottomRightCornerInBounds]; } 36 | - (void)testBottomRightCornerOutOfBounds { [self _testBottomRightCornerOutOfBounds]; } 37 | 38 | #pragma mark - Bottom left corner - 39 | - (void)testBottomLeftCornerInBounds { [self _testBottomLeftCornerInBounds]; } 40 | - (void)testBottomLeftCornerOutOfBounds { [self _testBottomLeftCornerOutOfBounds]; } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/KGHitTestButtonTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestButtonTests.m 3 | // KGHitTestingButtonsTests 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import "KGHitTestingButton.h" 12 | #import "KGHitTestBaseTest.h" 13 | 14 | @interface KGHitTestButtonTests : KGHitTestBaseTest 15 | 16 | @end 17 | 18 | @implementation KGHitTestButtonTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | // Put setup code here. This method is called before the invocation of each test method in the class. 23 | self.hitTestObject = [[KGHitTestingButton alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)]; 24 | } 25 | 26 | #pragma mark - Top left corner - 27 | - (void)testTopLeftCornerInBounds { [self _testTopLeftCornerInBounds]; } 28 | - (void)testTopLeftCornerOutOfBounds { [self _testTopLeftCornerOutOfBounds]; } 29 | 30 | #pragma mark - Top right corner - 31 | - (void)testTopRightCornerInBounds { [self _testTopRightCornerInBounds]; } 32 | - (void)testTopRightCornerOutOfBounds { [self _testTopRightCornerOutOfBounds]; } 33 | 34 | #pragma mark - Bottom right corner - 35 | - (void)testBottomRightCornerInBounds { [self _testBottomRightCornerInBounds]; } 36 | - (void)testBottomRightCornerOutOfBounds { [self _testBottomRightCornerOutOfBounds]; } 37 | 38 | #pragma mark - Bottom left corner - 39 | - (void)testBottomLeftCornerInBounds { [self _testBottomLeftCornerInBounds]; } 40 | - (void)testBottomLeftCornerOutOfBounds { [self _testBottomLeftCornerOutOfBounds]; } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/KGHitTestControlTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestControlTests.m 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 1/9/16. 6 | // 7 | // 8 | 9 | #import 10 | #import 11 | #import "KGHitTestingControl.h" 12 | #import "KGHitTestBaseTest.h" 13 | 14 | @interface KGHitTestControlTests : KGHitTestBaseTest 15 | 16 | @end 17 | 18 | @implementation KGHitTestControlTests 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | // Put setup code here. This method is called before the invocation of each test method in the class. 23 | self.hitTestObject = [[KGHitTestingControl alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)]; 24 | } 25 | 26 | #pragma mark - Top left corner - 27 | - (void)testTopLeftCornerInBounds { [self _testTopLeftCornerInBounds]; } 28 | - (void)testTopLeftCornerOutOfBounds { [self _testTopLeftCornerOutOfBounds]; } 29 | 30 | #pragma mark - Top right corner - 31 | - (void)testTopRightCornerInBounds { [self _testTopRightCornerInBounds]; } 32 | - (void)testTopRightCornerOutOfBounds { [self _testTopRightCornerOutOfBounds]; } 33 | 34 | #pragma mark - Bottom right corner - 35 | - (void)testBottomRightCornerInBounds { [self _testBottomRightCornerInBounds]; } 36 | - (void)testBottomRightCornerOutOfBounds { [self _testBottomRightCornerOutOfBounds]; } 37 | 38 | #pragma mark - Bottom left corner - 39 | - (void)testBottomLeftCornerInBounds { [self _testBottomLeftCornerInBounds]; } 40 | - (void)testBottomLeftCornerOutOfBounds { [self _testBottomLeftCornerOutOfBounds]; } 41 | 42 | @end 43 | -------------------------------------------------------------------------------- /KGHitTestingViews/UIView+KGHitTesting.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+KGHitTesting.h 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/28/16. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | 30 | @interface UIView (KGHitTesting) 31 | 32 | - (void)setMinimumHitTestWidth:(CGFloat)width height:(CGFloat)height; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingHelper.h 3 | // KGHitTestingViews 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | #import 30 | 31 | CGRect KGHitTestingBounds(CGRect bounds, CGFloat minimumHitTestWidth, CGFloat minimumHitTestHeight); -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingView.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingView.m 3 | // KGHitTestingViews 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import "KGHitTestingView.h" 29 | #import "KGHitTestingHelper.h" 30 | 31 | @implementation KGHitTestingView 32 | 33 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 34 | return CGRectContainsPoint(KGHitTestingBounds(self.bounds, self.minimumHitTestWidth, self.minimumHitTestHeight), point); 35 | } 36 | 37 | @end -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingButton.m 3 | // KGHitTestingViews 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import "KGHitTestingButton.h" 29 | #import "KGHitTestingHelper.h" 30 | 31 | @implementation KGHitTestingButton 32 | 33 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 34 | return CGRectContainsPoint(KGHitTestingBounds(self.bounds, self.minimumHitTestWidth, self.minimumHitTestHeight), point); 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingControl.m 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 1/9/16. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import "KGHitTestingControl.h" 29 | #import "KGHitTestingHelper.h" 30 | 31 | @implementation KGHitTestingControl 32 | 33 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 34 | return CGRectContainsPoint(KGHitTestingBounds(self.bounds, self.minimumHitTestWidth, self.minimumHitTestHeight), point); 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTesting.h: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTesting.h 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 5/10/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | #import 30 | 31 | @protocol KGHitTesting 32 | 33 | @required 34 | 35 | @property (nonatomic) IBInspectable CGFloat minimumHitTestWidth; 36 | @property (nonatomic) IBInspectable CGFloat minimumHitTestHeight; 37 | 38 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event; 39 | 40 | @end -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingHelper.m 3 | // KGHitTestingViews 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import "KGHitTestingHelper.h" 29 | 30 | CGRect KGHitTestingBounds(CGRect bounds, CGFloat minimumHitTestWidth, CGFloat minimumHitTestHeight) { 31 | 32 | CGRect hitTestingBounds = bounds; 33 | 34 | if (minimumHitTestWidth > bounds.size.width) { 35 | hitTestingBounds.size.width = minimumHitTestWidth; 36 | hitTestingBounds.origin.x -= (hitTestingBounds.size.width - bounds.size.width)/2; 37 | } 38 | 39 | if (minimumHitTestHeight > bounds.size.height) { 40 | hitTestingBounds.size.height = minimumHitTestHeight; 41 | hitTestingBounds.origin.y -= (hitTestingBounds.size.height - bounds.size.height)/2; 42 | } 43 | 44 | return hitTestingBounds; 45 | } -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingView.h: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingView.h 3 | // KGHitTestingViews 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | #import "KGHitTesting.h" 30 | 31 | @interface KGHitTestingView : UIView 32 | 33 | /** 34 | @desc The minimum width that will be used when hit testing is 35 | performed. 36 | 37 | Specifying a larger value than the bounds width will 38 | increase the hit area of the view. 39 | 40 | Specifying a smaller value than the bounds width will 41 | cause no effect - the bounds width will be used for hit 42 | testing. 43 | 44 | The default value causes no effect. 45 | */ 46 | @property (nonatomic) IBInspectable CGFloat minimumHitTestWidth; 47 | 48 | /** 49 | @desc The minimum height that will be used when hit testing is 50 | performed. 51 | 52 | Specifying a larger value than the bounds height will 53 | increase the hit area of the view. 54 | 55 | Specifying a smaller value than the bounds height will 56 | cause no effect - the bounds height will be used for hit 57 | testing. 58 | 59 | The default value causes no effect. 60 | */ 61 | @property (nonatomic) IBInspectable CGFloat minimumHitTestHeight; 62 | 63 | @end -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingButton.h 3 | // KGHitTestingViews 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | #import "KGHitTesting.h" 30 | 31 | @interface KGHitTestingButton : UIButton 32 | 33 | /** 34 | @desc The minimum width that will be used when hit testing is 35 | performed. 36 | 37 | Specifying a larger value than the bounds width will 38 | increase the hit area of the view. 39 | 40 | Specifying a smaller value than the bounds width will 41 | cause no effect - the bounds width will be used for hit 42 | testing. 43 | 44 | The default value causes no effect. 45 | */ 46 | @property (nonatomic) IBInspectable CGFloat minimumHitTestWidth; 47 | 48 | /** 49 | @desc The minimum height that will be used when hit testing is 50 | performed. 51 | 52 | Specifying a larger value than the bounds height will 53 | increase the hit area of the view. 54 | 55 | Specifying a smaller value than the bounds height will 56 | cause no effect - the bounds height will be used for hit 57 | testing. 58 | 59 | The default value causes no effect. 60 | */ 61 | @property (nonatomic) IBInspectable CGFloat minimumHitTestHeight; 62 | 63 | @end -------------------------------------------------------------------------------- /KGHitTestingViews/KGHitTestingControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestingControl.h 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 1/9/16. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import 29 | #import "KGHitTesting.h" 30 | 31 | @interface KGHitTestingControl : UIControl 32 | 33 | /** 34 | @desc The minimum width that will be used when hit testing is 35 | performed. 36 | 37 | Specifying a larger value than the bounds width will 38 | increase the hit area of the view. 39 | 40 | Specifying a smaller value than the bounds width will 41 | cause no effect - the bounds width will be used for hit 42 | testing. 43 | 44 | The default value causes no effect. 45 | */ 46 | @property (nonatomic) IBInspectable CGFloat minimumHitTestWidth; 47 | 48 | /** 49 | @desc The minimum height that will be used when hit testing is 50 | performed. 51 | 52 | Specifying a larger value than the bounds height will 53 | increase the hit area of the view. 54 | 55 | Specifying a smaller value than the bounds height will 56 | cause no effect - the bounds height will be used for hit 57 | testing. 58 | 59 | The default value causes no effect. 60 | */ 61 | @property (nonatomic) IBInspectable CGFloat minimumHitTestHeight; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/KGHitTestBaseTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestBaseTest.m 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 5/10/15. 6 | // 7 | // 8 | 9 | #import "KGHitTestBaseTest.h" 10 | 11 | @implementation KGHitTestBaseTest 12 | 13 | - (void)setUp { 14 | [super setUp]; 15 | // Override setup in each subclass. 16 | } 17 | 18 | - (void)tearDown { 19 | self.hitTestObject = nil; 20 | [super tearDown]; 21 | } 22 | 23 | #pragma mark - Helpers - 24 | 25 | - (id)increaseHitTestAreaForHitTestObject:(id)hitTestObject { 26 | hitTestObject.minimumHitTestHeight = kMinimumHitTestHeight; 27 | hitTestObject.minimumHitTestWidth = kMinimumHitTestWidth; 28 | return hitTestObject; 29 | } 30 | 31 | #pragma mark - Top left corner - 32 | 33 | - (void)_testTopLeftCornerInBounds { 34 | XCTAssert([self.hitTestObject pointInside:CGPointMake(0, 0) withEvent:nil]); 35 | XCTAssert([[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(-kWidthOffset, -kHeightOffset) withEvent:nil]); 36 | } 37 | 38 | - (void)_testTopLeftCornerOutOfBounds { 39 | XCTAssert(![self.hitTestObject pointInside:CGPointMake(-0.1, -0.1) withEvent:nil]); 40 | XCTAssert(![[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(-kWidthOffset-0.1, -kHeightOffset-0.1) withEvent:nil]); 41 | } 42 | 43 | #pragma mark - Top right corner - 44 | 45 | - (void)_testTopRightCornerInBounds { 46 | XCTAssert([self.hitTestObject pointInside:CGPointMake(kWidth-0.1, 0.0) withEvent:nil]); 47 | XCTAssert([[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(kWidth+kWidthOffset-0.1, kHeightOffset) withEvent:nil]); 48 | } 49 | 50 | - (void)_testTopRightCornerOutOfBounds { 51 | XCTAssert(![self.hitTestObject pointInside:CGPointMake(kWidth+0.1, 0.1) withEvent:nil]); 52 | XCTAssert(![[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(kWidth+kWidthOffset+0.1, kHeightOffset+0.1) withEvent:nil]); 53 | } 54 | 55 | #pragma mark - Bottom right corner - 56 | 57 | - (void)_testBottomRightCornerInBounds { 58 | XCTAssert([self.hitTestObject pointInside:CGPointMake(kWidth-0.1, kHeight-0.1) withEvent:nil]); 59 | XCTAssert([[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(kWidth+kWidthOffset-0.1, kHeight+kHeightOffset-0.1) withEvent:nil]); 60 | } 61 | 62 | - (void)_testBottomRightCornerOutOfBounds { 63 | XCTAssert(![self.hitTestObject pointInside:CGPointMake(kWidth+0.1, kHeight+0.1) withEvent:nil]); 64 | XCTAssert(![[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(kWidth+kWidthOffset+0.1, kHeight+kHeightOffset+0.1) withEvent:nil]); 65 | } 66 | 67 | #pragma mark - Bottom left corner - 68 | 69 | - (void)_testBottomLeftCornerInBounds { 70 | XCTAssert([self.hitTestObject pointInside:CGPointMake(0, kHeight-0.1) withEvent:nil]); 71 | XCTAssert([[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(-kWidthOffset, kHeight+kHeightOffset-0.1) withEvent:nil]); 72 | } 73 | 74 | - (void)_testBottomLeftCornerOutOfBounds { 75 | XCTAssert(![self.hitTestObject pointInside:CGPointMake(0, kHeight+0.1) withEvent:nil]); 76 | XCTAssert(![[self increaseHitTestAreaForHitTestObject:self.hitTestObject] pointInside:CGPointMake(-kWidthOffset-0.1, kHeight+kHeightOffset+0.1) withEvent:nil]); 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | **KGHitTestingViews** is a small helper library to **effortlessly increase the hit test area of a view**. 3 | 4 | It's common to display buttons as small as 20 x 20 points. However, [iOS User Interface Guidelines](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/LayoutandAppearance.html) recommends that all tappable controls have at least a 44 x 44 point hit area. A common solution to this problem involves making a large button containing a small image, but this solution impacts positioning constraints relative to the small image and makes the layout generally more awkward. **KGHitTestingViews** aims to solve this problem in a simple manner. 5 | 6 | ## Installation 7 | 8 | To install using [CocoaPods](https://github.com/cocoapods/cocoapods), add the following to your project Podfile: 9 | 10 | ```ruby 11 | pod 'KGHitTestingViews' 12 | ``` 13 | 14 | Alternatively, drag and drop all of the files in the KGHitTestingViews folder into your Xcode project, agreeing to copy files if needed. 15 | 16 | ## How to Use 17 | 18 | ### In Code (Category): 19 | The easiest way to use **KGHitTestingViews** is to use the `UIView` category: `"UIView+KGHitTesting.h"`. No subclassing required! 20 | 21 | ```objective-c 22 | @interface UIView (KGHitTesting) 23 | - (void)setMinimumHitTestWidth:(CGFloat)width height:(CGFloat)height; 24 | @end 25 | ``` 26 | 27 | ### In Code (Subclassing): 28 | Subclass `KGHitTestingView` or `KGHitTestingControl` or `KGHitTestingButton` and set `minimumHitTestHeight` and `minimumHitTestWidth` to the desired size of the hit test area: 29 | 30 | ```objective-c 31 | @interface ButtonSubclass : KGHitTestingButton 32 | @end 33 | ``` 34 | ```objective-c 35 | - (void)setup { 36 | self.minimumHitTestWidth = 44.0; 37 | self.minimumHitTestHeight = 44.0; 38 | } 39 | ``` 40 | 41 | ### In Interface Builder (Subclassing): 42 | Subclass `KGHitTestingView` or `KGHitTestingControl` or `KGHitTestingButton` in the "Identify Inspector" and set the width and height values in the "Attributes Inspector." 43 | 44 | ![](Images/InterfaceBuilderSubclassExample.png) 45 | 46 | ### Important note: 47 | The height and width of the hit test area are *minimums*. If you specify a smaller hit area width or height than what the bounds specify, the bounds width or height will be used. 48 | 49 | ## Swift Support 50 | KGHitTestingViews works with Swift! Just follow the "How to Use" section. 51 | 52 | ## FAQ 53 | ###What is hit testing? 54 | 55 | "The process of determining whether a user-controlled cursor (such as a mouse cursor or touch-point on a touch-screen interface) intersects a given graphical object (such as a shape, line, or curve) drawn on the screen." - [Wikipedia](http://en.wikipedia.org/wiki/Hit-testing) 56 | 57 | ###Why override `-pointInside:`? I thought this is hit testing. 58 | Hit test (`-hitTest:`) is a more-complex algorithmic test that involves looping through subviews. Point inside (`-pointInside:`) is a simple geometric test which checks whether a `CGPoint` is within the bounds of a given view. For the purposes of increasing the touch area, we only need to modify the geometric calculation. For more, check out [this](http://smnh.me/hit-testing-in-ios/) nicely written blog about hit-testing. 59 | 60 | ## Credits 61 | Created and maintained by Krisjanis Gaidis. 62 | 63 | Contributions: 64 | 65 | * IBInspectable addition by [mailworks](https://github.com/mailworks). 66 | 67 | ## License 68 | KGHitTestingViews is released under the MIT license. See LICENSE for details. -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/2/15. 6 | // Copyright (c) 2015 Krisjanis Gaidis. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "KGHitTestingButton.h" 11 | #import "KGHitTestingHelper.h" 12 | 13 | static NSString *const kWidthKeyPath = @"button.minimumHitTestWidth"; 14 | static NSString *const kHeightKeyPath = @"button.minimumHitTestHeight"; 15 | 16 | @interface ViewController () 17 | 18 | @property (weak, nonatomic) IBOutlet KGHitTestingButton *button; 19 | @property (weak, nonatomic) IBOutlet UIView *hitTestAreaView; 20 | 21 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *hitTestAreaViewHeightConstraint; 22 | @property (weak, nonatomic) IBOutlet NSLayoutConstraint *hitTestAreaViewWidthConstraint; 23 | 24 | @property (weak, nonatomic) IBOutlet UILabel *widthLabel; 25 | @property (weak, nonatomic) IBOutlet UISlider *widthSlider; 26 | @property (weak, nonatomic) IBOutlet UILabel *heightLabel; 27 | @property (weak, nonatomic) IBOutlet UISlider *heightSlider; 28 | 29 | @property (weak, nonatomic) IBOutlet UILabel *hitTestingBoundsLabel; 30 | 31 | @end 32 | 33 | @implementation ViewController 34 | 35 | #pragma mark - Lifecycle - 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | [self registerForKVO]; 40 | [self setupSliders]; 41 | 42 | // Important Note: 43 | // The 'minimumHitTestWidth' and 'minimumHitTestHeight' are first set as the 44 | // IBInspectable "User Defined Runtime Attributes" in "Main.storyboard" for 45 | // the UIButton. 46 | 47 | [self updateOutlets]; 48 | } 49 | 50 | - (void)dealloc { 51 | [self unregisterForKVO]; 52 | } 53 | 54 | - (BOOL)prefersStatusBarHidden { 55 | return YES; 56 | } 57 | 58 | #pragma mark - Helpers - 59 | 60 | - (void)setupSliders { 61 | // Autolayout doesn't seem to like height/width values of 0. 62 | self.widthSlider.minimumValue = 1.0; 63 | self.heightSlider.minimumValue = 1.0; 64 | self.widthSlider.maximumValue = 250.0; 65 | self.heightSlider.maximumValue = 250.0; 66 | } 67 | 68 | - (void)updateOutlets { 69 | 70 | // Update width 71 | self.hitTestAreaViewWidthConstraint.constant = self.button.minimumHitTestWidth; 72 | self.widthLabel.text = [NSString stringWithFormat:@"minimumHitTestWidth: %@", @(self.button.minimumHitTestWidth)]; 73 | 74 | // Update height 75 | self.hitTestAreaViewHeightConstraint.constant = self.button.minimumHitTestHeight; 76 | self.heightLabel.text = [NSString stringWithFormat:@"minimumHitTestHeight: %@", @(self.button.minimumHitTestHeight)]; 77 | 78 | // Update bounds 79 | self.hitTestingBoundsLabel.text = [NSString stringWithFormat:@"HitTestingBounds:\n%@", NSStringFromCGRect(KGHitTestingBounds(self.button.bounds, self.button.minimumHitTestWidth, self.button.minimumHitTestHeight))]; 80 | 81 | // Update sliders 82 | self.widthSlider.value = self.button.minimumHitTestWidth; 83 | self.heightSlider.value = self.button.minimumHitTestHeight; 84 | } 85 | 86 | #pragma mark - KVO Observing - 87 | 88 | - (void)registerForKVO { 89 | [self addObserver:self forKeyPath:kWidthKeyPath options:NSKeyValueObservingOptionNew context:nil]; 90 | [self addObserver:self forKeyPath:kHeightKeyPath options:NSKeyValueObservingOptionNew context:nil]; 91 | } 92 | 93 | - (void)unregisterForKVO { 94 | [self removeObserver:self forKeyPath:kWidthKeyPath context:nil]; 95 | [self removeObserver:self forKeyPath:kHeightKeyPath context:nil]; 96 | } 97 | 98 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 99 | [self updateOutlets]; 100 | } 101 | 102 | #pragma mark - Slider actions - 103 | 104 | - (IBAction)didChangeValueOfWidthSlider:(UISlider *)widthSlider { 105 | [widthSlider setValue:(NSUInteger)(widthSlider.value + 0.5) animated:NO]; 106 | self.button.minimumHitTestWidth = widthSlider.value; 107 | } 108 | 109 | - (IBAction)didChangeValueOfHeightSlider:(UISlider *)heightSlider { 110 | [heightSlider setValue:(NSUInteger)(heightSlider.value + 0.5) animated:NO]; 111 | self.button.minimumHitTestHeight = heightSlider.value; 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /KGHitTestingViews/UIView+KGHitTesting.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+KGHitTesting.m 3 | // KGHitTestingViewsExample 4 | // 5 | // Created by Krisjanis Gaidis on 5/28/16. 6 | // 7 | // This code is distributed under the terms and conditions of the MIT license. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to 11 | // deal in the Software without restriction, including without limitation the 12 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 13 | // sell copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | // IN THE SOFTWARE. 26 | // 27 | 28 | #import "UIView+KGHitTesting.h" 29 | #import "KGHitTestingHelper.h" 30 | #import 31 | 32 | static NSString * const kKGHitTestingClassPrefix = @"KGHitTesting_"; 33 | 34 | @implementation UIView (KGHitTesting) 35 | 36 | - (void)setMinimumHitTestWidth:(CGFloat)width height:(CGFloat)height { 37 | [self kg_setMinimumHitTestWidth:width]; 38 | [self kg_setMinimumHitTestHeight:height]; 39 | 40 | Class currentClass = object_getClass(self); 41 | 42 | if (isKVOSubclass(self)) { 43 | // We do NOT create a special class if the object was already 'isa-swizzled' by KVO. 44 | // We just reuse the special KVO class. 45 | [UIView kg_addHitTestImplementationToClass:currentClass]; 46 | } 47 | else if (![NSStringFromClass(currentClass) hasPrefix:kKGHitTestingClassPrefix]) { 48 | const char *newClassName = [[kKGHitTestingClassPrefix stringByAppendingString:NSStringFromClass(currentClass)] UTF8String]; 49 | Class newClass = objc_getClass(newClassName); 50 | if (newClass == nil) { // 'KGHitTesting_[self class]' does not exist yet, so lets create it! 51 | newClass = objc_allocateClassPair(currentClass, newClassName, 0); 52 | [UIView kg_addHitTestImplementationToClass:newClass]; 53 | objc_registerClassPair(newClass); 54 | } 55 | object_setClass(self, newClass); 56 | } 57 | } 58 | 59 | + (void)kg_addHitTestImplementationToClass:(Class)class { 60 | NSAssert([class isSubclassOfClass:[UIView class]], @"We should only be adding KGHitTesting implementation to a UIView subclass."); 61 | 62 | Method kg_pointInsideMethod = class_getInstanceMethod(class, @selector(kg_pointInside:withEvent:)); 63 | IMP kg_pointInsideImplementation = class_getMethodImplementation(class, @selector(kg_pointInside:withEvent:)); 64 | 65 | class_addMethod(class, @selector(pointInside:withEvent:), kg_pointInsideImplementation, method_getTypeEncoding(kg_pointInsideMethod)); 66 | } 67 | 68 | - (BOOL)kg_pointInside:(CGPoint)point withEvent:(UIEvent *)event { 69 | if (objc_getAssociatedObject(self, @selector(kg_minimumHitTestWidth))) { 70 | return CGRectContainsPoint(KGHitTestingBounds(self.bounds, [self kg_minimumHitTestWidth], [self kg_minimumHitTestHeight]), point); 71 | } 72 | 73 | // If the Object was using KVO before using 'setMinimumHitTestWidth:height:', then 74 | // after using 'setMinimumHitTestWidth:height:', its class remains something like 'KVO_Object'. 75 | // This means that our custom 'pointInside:withEvent:' will get called for EVERY object that 76 | // has the class 'KVO_Object' - even if they never called 'setMinimumHitTestWidth:height:' 77 | // on that object. In those cases, the associated object ('kg_minimumHitTestWidth') will not be set. 78 | // When we notice that case, we make sure to call the ORIGINAL 'pointInside:withEvent:' so 79 | // there would be no surprises. 80 | NSAssert(isKVOSubclass(self), @"Logic error. The only time a custom 'pointInside:withEvent:' should get to this point is if the object is using KVO."); 81 | Class actualClass = [self class]; 82 | IMP originalPointInside = class_getMethodImplementation(actualClass, @selector(pointInside:withEvent:)); 83 | return ((BOOL (*)(id, SEL, CGPoint, UIEvent *))originalPointInside)(self, _cmd, point, event); 84 | } 85 | 86 | #pragma mark - Properties - 87 | 88 | - (void)kg_setMinimumHitTestWidth:(CGFloat)minimumHitTestWidth { 89 | objc_setAssociatedObject(self, @selector(kg_minimumHitTestWidth), @(minimumHitTestWidth), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 90 | } 91 | 92 | - (CGFloat)kg_minimumHitTestWidth { 93 | return [objc_getAssociatedObject(self, @selector(kg_minimumHitTestWidth)) floatValue]; 94 | } 95 | 96 | - (void)kg_setMinimumHitTestHeight:(CGFloat)minimumHitTestHeight { 97 | objc_setAssociatedObject(self, @selector(kg_minimumHitTestHeight), @(minimumHitTestHeight), OBJC_ASSOCIATION_RETAIN_NONATOMIC); 98 | } 99 | 100 | - (CGFloat)kg_minimumHitTestHeight { 101 | return [objc_getAssociatedObject(self, @selector(kg_minimumHitTestHeight)) floatValue]; 102 | } 103 | 104 | #pragma mark - Runtime helpers - 105 | // A lot of inspiration from: 106 | // https://github.com/mikeash/MAZeroingWeakRef 107 | 108 | static BOOL isKVOSubclass(id object) { 109 | // [self class] gets overriden by the KVO class to return the super class 110 | // However, 'class_getSuperclass' uncovers the actual class (which is 'NSKVONotifying_[self class]') 111 | return [object class] == class_getSuperclass(object_getClass(object)); 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests/KGHitTestCategoryTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // KGHitTestCategoryTests.m 3 | // KGHitTestingViewsTests 4 | // 5 | // Created by Krisjanis Gaidis on 5/29/16. 6 | // 7 | // 8 | 9 | #import 10 | #import "KGHitTestBaseTest.h" 11 | #import "UIView+KGHitTesting.h" 12 | 13 | @interface CustomViewSubclass : UIView 14 | @property (nonatomic) BOOL calledCustomPointInsideImplementation; 15 | @end 16 | 17 | @implementation CustomViewSubclass 18 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 19 | self.calledCustomPointInsideImplementation = YES; 20 | return [super pointInside:point withEvent:event]; 21 | } 22 | @end 23 | 24 | @interface KGHitTestCategoryTests : KGHitTestBaseTest 25 | 26 | @end 27 | 28 | @implementation KGHitTestCategoryTests 29 | 30 | - (void)setUp { 31 | [super setUp]; 32 | self.hitTestObject = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kWidth, kHeight)]; 33 | } 34 | 35 | - (id)increaseHitTestAreaForHitTestObject:(id)hitTestObject { 36 | [((UIView *)hitTestObject) setMinimumHitTestWidth:kMinimumHitTestWidth height:kMinimumHitTestHeight]; 37 | return hitTestObject; 38 | } 39 | 40 | #pragma mark - General tests - 41 | 42 | - (void)testView_whenSettingMinimumWidthHeight_classNameShouldChange { 43 | UIView *view = [UIView new]; 44 | Class initialClass = [view class]; 45 | [view setMinimumHitTestWidth:10 height:10]; 46 | Class newClass = [view class]; 47 | XCTAssert(initialClass != newClass); 48 | } 49 | 50 | - (void)testView_whenSettingMinimumWidthHeight_hitAreaShouldAdjust { 51 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 52 | XCTAssertNotNil([view hitTest:CGPointMake(9, 9) withEvent:nil]); 53 | XCTAssertNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 54 | [view setMinimumHitTestWidth:44 height:44]; 55 | XCTAssertNotNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 56 | [view setMinimumHitTestWidth:10 height:10]; 57 | XCTAssertNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 58 | [view setMinimumHitTestWidth:44 height:44]; 59 | XCTAssertNotNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 60 | } 61 | 62 | - (void)testView_whenSettingMinimumWidthHeight_classNameShouldNotChangeMultipleTimes { 63 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 64 | Class initialClass = [view class]; 65 | [view setMinimumHitTestWidth:10 height:10]; 66 | Class newClass = [view class]; 67 | XCTAssert(initialClass != newClass); 68 | [view setMinimumHitTestWidth:10 height:10]; 69 | XCTAssert([NSStringFromClass(newClass) isEqualToString:NSStringFromClass([view class])]); 70 | } 71 | 72 | #pragma mark - KVO tests - 73 | 74 | - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { } 75 | 76 | - (void)testView_whenRegisteringForKVOAfterSettingHitTestWidthHeight_thereShouldBeNoCrashes { 77 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 78 | [view setMinimumHitTestWidth:20 height:20]; 79 | [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 80 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 81 | [view removeObserver:self forKeyPath:@"frame"]; 82 | } 83 | 84 | - (void)testView_whenRegisteringForKVOBeforeSettingHitTestWidthHeight_thereShouldBeNoCrashes { 85 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 86 | [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 87 | [view setMinimumHitTestWidth:20 height:20]; 88 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 89 | [view removeObserver:self forKeyPath:@"frame"]; 90 | } 91 | 92 | - (void)testView_whenRegisteringForKVOAndUsingSameViewMultipleTimes_thereShouldBeNoCrashes { 93 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 94 | [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 95 | [view setMinimumHitTestWidth:20 height:20]; 96 | 97 | UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 98 | [view2 addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 99 | XCTAssert([view2 pointInside:CGPointMake(9, 9) withEvent:nil]); 100 | XCTAssert(![view2 pointInside:CGPointMake(19, 19) withEvent:nil]); 101 | 102 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 103 | [view removeObserver:self forKeyPath:@"frame"]; 104 | [view2 removeObserver:self forKeyPath:@"frame"]; 105 | } 106 | 107 | - (void)testView_whenRegisteringForKVOBeforeSettingMinimumWidthHeight_hitAreaShouldAdjust { 108 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 109 | [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 110 | XCTAssertNotNil([view hitTest:CGPointMake(9, 9) withEvent:nil]); 111 | XCTAssertNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 112 | [view setMinimumHitTestWidth:44 height:44]; 113 | XCTAssertNotNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 114 | [view setMinimumHitTestWidth:10 height:10]; 115 | XCTAssertNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 116 | [view setMinimumHitTestWidth:44 height:44]; 117 | XCTAssertNotNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 118 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 119 | [view removeObserver:self forKeyPath:@"frame"]; 120 | } 121 | 122 | - (void)testView_whenRegisteringForKVOAfterSettingMinimumWidthHeight_hitAreaShouldAdjust { 123 | UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 124 | XCTAssertNotNil([view hitTest:CGPointMake(9, 9) withEvent:nil]); 125 | XCTAssertNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 126 | [view setMinimumHitTestWidth:44 height:44]; 127 | [view addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil]; 128 | XCTAssertNotNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 129 | [view setMinimumHitTestWidth:10 height:10]; 130 | XCTAssertNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 131 | [view setMinimumHitTestWidth:44 height:44]; 132 | XCTAssertNotNil([view hitTest:CGPointMake(20, 20) withEvent:nil]); 133 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 134 | [view removeObserver:self forKeyPath:@"frame"]; 135 | } 136 | 137 | - (void)testCustomViewSubclass_afterRegisteringKVOAndUsingHitTesting_customPointInsideImplementationShouldGetCalled { 138 | CustomViewSubclass *customView = [[CustomViewSubclass alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 139 | [customView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 140 | [customView setMinimumHitTestWidth:20 height:20]; 141 | [customView pointInside:CGPointZero withEvent:nil]; 142 | XCTAssert(!customView.calledCustomPointInsideImplementation, @"The overriden 'pointInside:withEvent' should be called instead."); 143 | 144 | CustomViewSubclass *customView2 = [[CustomViewSubclass alloc] initWithFrame:CGRectMake(0, 0, 10, 10)]; 145 | [customView2 addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew context:nil]; 146 | [customView2 pointInside:CGPointZero withEvent:nil]; 147 | XCTAssert(customView2.calledCustomPointInsideImplementation); 148 | 149 | [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.01]]; 150 | [customView removeObserver:self forKeyPath:@"frame"]; 151 | [customView2 removeObserver:self forKeyPath:@"frame"]; 152 | } 153 | 154 | #pragma mark - Top left corner - 155 | - (void)testTopLeftCornerInBounds { [self _testTopLeftCornerInBounds]; } 156 | - (void)testTopLeftCornerOutOfBounds { [self _testTopLeftCornerOutOfBounds]; } 157 | 158 | #pragma mark - Top right corner - 159 | - (void)testTopRightCornerInBounds { [self _testTopRightCornerInBounds]; } 160 | - (void)testTopRightCornerOutOfBounds { [self _testTopRightCornerOutOfBounds]; } 161 | 162 | #pragma mark - Bottom right corner - 163 | - (void)testBottomRightCornerInBounds { [self _testBottomRightCornerInBounds]; } 164 | - (void)testBottomRightCornerOutOfBounds { [self _testBottomRightCornerOutOfBounds]; } 165 | 166 | #pragma mark - Bottom left corner - 167 | - (void)testBottomLeftCornerInBounds { [self _testBottomLeftCornerInBounds]; } 168 | - (void)testBottomLeftCornerOutOfBounds { [self _testBottomLeftCornerOutOfBounds]; } 169 | 170 | @end 171 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 74 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /Tests/KGHitTestingViewsTests.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 014191B11B002D9B00262180 /* KGHitTestBaseTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 014191AD1B0025C900262180 /* KGHitTestBaseTest.m */; }; 11 | 01596BDA1CFBD7D4006241B7 /* UIView+KGHitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 01596BD91CFBD7D4006241B7 /* UIView+KGHitTesting.m */; }; 12 | 01596BDC1CFBD85C006241B7 /* KGHitTestCategoryTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01596BDB1CFBD85C006241B7 /* KGHitTestCategoryTests.m */; }; 13 | 01943F3D1AF5997A005642B0 /* KGHitTestingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 01943F3A1AF5997A005642B0 /* KGHitTestingButton.m */; }; 14 | 01943F3E1AF5997A005642B0 /* KGHitTestingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 01943F3B1AF5997A005642B0 /* KGHitTestingHelper.m */; }; 15 | 01943F3F1AF5997A005642B0 /* KGHitTestingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 01943F3C1AF5997A005642B0 /* KGHitTestingView.m */; }; 16 | 01943F411AF59CE6005642B0 /* KGHitTestViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01943F401AF59CE6005642B0 /* KGHitTestViewTests.m */; }; 17 | 01943F431AF5A573005642B0 /* KGHitTestButtonTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 01943F421AF5A573005642B0 /* KGHitTestButtonTests.m */; }; 18 | 0197EEA01C41DB1D00ACC75F /* KGHitTestingControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 0197EE9F1C41DB1D00ACC75F /* KGHitTestingControl.m */; }; 19 | 0197EEA21C41DB9800ACC75F /* KGHitTestControlTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0197EEA11C41DB9800ACC75F /* KGHitTestControlTests.m */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXFileReference section */ 23 | 014191AC1B0025C900262180 /* KGHitTestBaseTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestBaseTest.h; sourceTree = ""; }; 24 | 014191AD1B0025C900262180 /* KGHitTestBaseTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestBaseTest.m; sourceTree = ""; }; 25 | 014191B01B00298900262180 /* KGHitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTesting.h; sourceTree = ""; }; 26 | 01596BD81CFBD7D4006241B7 /* UIView+KGHitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+KGHitTesting.h"; sourceTree = ""; }; 27 | 01596BD91CFBD7D4006241B7 /* UIView+KGHitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+KGHitTesting.m"; sourceTree = ""; }; 28 | 01596BDB1CFBD85C006241B7 /* KGHitTestCategoryTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestCategoryTests.m; sourceTree = ""; }; 29 | 01943F371AF5997A005642B0 /* KGHitTestingButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingButton.h; sourceTree = ""; }; 30 | 01943F381AF5997A005642B0 /* KGHitTestingHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingHelper.h; sourceTree = ""; }; 31 | 01943F391AF5997A005642B0 /* KGHitTestingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingView.h; sourceTree = ""; }; 32 | 01943F3A1AF5997A005642B0 /* KGHitTestingButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingButton.m; sourceTree = ""; }; 33 | 01943F3B1AF5997A005642B0 /* KGHitTestingHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingHelper.m; sourceTree = ""; }; 34 | 01943F3C1AF5997A005642B0 /* KGHitTestingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingView.m; sourceTree = ""; }; 35 | 01943F401AF59CE6005642B0 /* KGHitTestViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestViewTests.m; sourceTree = ""; }; 36 | 01943F421AF5A573005642B0 /* KGHitTestButtonTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestButtonTests.m; sourceTree = ""; }; 37 | 0197EE9E1C41DB1D00ACC75F /* KGHitTestingControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingControl.h; sourceTree = ""; }; 38 | 0197EE9F1C41DB1D00ACC75F /* KGHitTestingControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingControl.m; sourceTree = ""; }; 39 | 0197EEA11C41DB9800ACC75F /* KGHitTestControlTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestControlTests.m; sourceTree = ""; }; 40 | 01B610DF1AF56269001BCC44 /* KGHitTestingViewsTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KGHitTestingViewsTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 01B610E31AF56269001BCC44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 42 | /* End PBXFileReference section */ 43 | 44 | /* Begin PBXFrameworksBuildPhase section */ 45 | 01B610DC1AF56269001BCC44 /* Frameworks */ = { 46 | isa = PBXFrameworksBuildPhase; 47 | buildActionMask = 2147483647; 48 | files = ( 49 | ); 50 | runOnlyForDeploymentPostprocessing = 0; 51 | }; 52 | /* End PBXFrameworksBuildPhase section */ 53 | 54 | /* Begin PBXGroup section */ 55 | 01943F361AF59950005642B0 /* KGHitTestingViews */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 014191B01B00298900262180 /* KGHitTesting.h */, 59 | 01943F381AF5997A005642B0 /* KGHitTestingHelper.h */, 60 | 01943F3B1AF5997A005642B0 /* KGHitTestingHelper.m */, 61 | 01943F391AF5997A005642B0 /* KGHitTestingView.h */, 62 | 01943F3C1AF5997A005642B0 /* KGHitTestingView.m */, 63 | 01943F371AF5997A005642B0 /* KGHitTestingButton.h */, 64 | 01943F3A1AF5997A005642B0 /* KGHitTestingButton.m */, 65 | 0197EE9E1C41DB1D00ACC75F /* KGHitTestingControl.h */, 66 | 0197EE9F1C41DB1D00ACC75F /* KGHitTestingControl.m */, 67 | 01596BD81CFBD7D4006241B7 /* UIView+KGHitTesting.h */, 68 | 01596BD91CFBD7D4006241B7 /* UIView+KGHitTesting.m */, 69 | ); 70 | name = KGHitTestingViews; 71 | path = ../../KGHitTestingViews; 72 | sourceTree = ""; 73 | }; 74 | 01B610D41AF5622B001BCC44 = { 75 | isa = PBXGroup; 76 | children = ( 77 | 01B610E11AF56269001BCC44 /* KGHitTestingViewsTests */, 78 | 01B610E01AF56269001BCC44 /* Products */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | 01B610E01AF56269001BCC44 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 01B610DF1AF56269001BCC44 /* KGHitTestingViewsTests.xctest */, 86 | ); 87 | name = Products; 88 | sourceTree = ""; 89 | }; 90 | 01B610E11AF56269001BCC44 /* KGHitTestingViewsTests */ = { 91 | isa = PBXGroup; 92 | children = ( 93 | 014191AC1B0025C900262180 /* KGHitTestBaseTest.h */, 94 | 014191AD1B0025C900262180 /* KGHitTestBaseTest.m */, 95 | 01943F401AF59CE6005642B0 /* KGHitTestViewTests.m */, 96 | 01943F421AF5A573005642B0 /* KGHitTestButtonTests.m */, 97 | 0197EEA11C41DB9800ACC75F /* KGHitTestControlTests.m */, 98 | 01596BDB1CFBD85C006241B7 /* KGHitTestCategoryTests.m */, 99 | 01943F361AF59950005642B0 /* KGHitTestingViews */, 100 | 01B610E21AF56269001BCC44 /* Supporting Files */, 101 | ); 102 | path = KGHitTestingViewsTests; 103 | sourceTree = ""; 104 | }; 105 | 01B610E21AF56269001BCC44 /* Supporting Files */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 01B610E31AF56269001BCC44 /* Info.plist */, 109 | ); 110 | name = "Supporting Files"; 111 | sourceTree = ""; 112 | }; 113 | /* End PBXGroup section */ 114 | 115 | /* Begin PBXNativeTarget section */ 116 | 01B610DE1AF56269001BCC44 /* KGHitTestingViewsTests */ = { 117 | isa = PBXNativeTarget; 118 | buildConfigurationList = 01B610E61AF56269001BCC44 /* Build configuration list for PBXNativeTarget "KGHitTestingViewsTests" */; 119 | buildPhases = ( 120 | 01B610DB1AF56269001BCC44 /* Sources */, 121 | 01B610DC1AF56269001BCC44 /* Frameworks */, 122 | 01B610DD1AF56269001BCC44 /* Resources */, 123 | ); 124 | buildRules = ( 125 | ); 126 | dependencies = ( 127 | ); 128 | name = KGHitTestingViewsTests; 129 | productName = KGHitTestingViewsTests; 130 | productReference = 01B610DF1AF56269001BCC44 /* KGHitTestingViewsTests.xctest */; 131 | productType = "com.apple.product-type.bundle.unit-test"; 132 | }; 133 | /* End PBXNativeTarget section */ 134 | 135 | /* Begin PBXProject section */ 136 | 01B610D51AF5622B001BCC44 /* Project object */ = { 137 | isa = PBXProject; 138 | attributes = { 139 | LastUpgradeCheck = 0630; 140 | TargetAttributes = { 141 | 01B610DE1AF56269001BCC44 = { 142 | CreatedOnToolsVersion = 6.3.1; 143 | }; 144 | }; 145 | }; 146 | buildConfigurationList = 01B610D81AF5622B001BCC44 /* Build configuration list for PBXProject "KGHitTestingViewsTests" */; 147 | compatibilityVersion = "Xcode 3.2"; 148 | developmentRegion = English; 149 | hasScannedForEncodings = 0; 150 | knownRegions = ( 151 | en, 152 | ); 153 | mainGroup = 01B610D41AF5622B001BCC44; 154 | productRefGroup = 01B610E01AF56269001BCC44 /* Products */; 155 | projectDirPath = ""; 156 | projectRoot = ""; 157 | targets = ( 158 | 01B610DE1AF56269001BCC44 /* KGHitTestingViewsTests */, 159 | ); 160 | }; 161 | /* End PBXProject section */ 162 | 163 | /* Begin PBXResourcesBuildPhase section */ 164 | 01B610DD1AF56269001BCC44 /* Resources */ = { 165 | isa = PBXResourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXResourcesBuildPhase section */ 172 | 173 | /* Begin PBXSourcesBuildPhase section */ 174 | 01B610DB1AF56269001BCC44 /* Sources */ = { 175 | isa = PBXSourcesBuildPhase; 176 | buildActionMask = 2147483647; 177 | files = ( 178 | 01943F411AF59CE6005642B0 /* KGHitTestViewTests.m in Sources */, 179 | 01596BDC1CFBD85C006241B7 /* KGHitTestCategoryTests.m in Sources */, 180 | 0197EEA01C41DB1D00ACC75F /* KGHitTestingControl.m in Sources */, 181 | 01943F3D1AF5997A005642B0 /* KGHitTestingButton.m in Sources */, 182 | 01943F3F1AF5997A005642B0 /* KGHitTestingView.m in Sources */, 183 | 01943F431AF5A573005642B0 /* KGHitTestButtonTests.m in Sources */, 184 | 0197EEA21C41DB9800ACC75F /* KGHitTestControlTests.m in Sources */, 185 | 014191B11B002D9B00262180 /* KGHitTestBaseTest.m in Sources */, 186 | 01596BDA1CFBD7D4006241B7 /* UIView+KGHitTesting.m in Sources */, 187 | 01943F3E1AF5997A005642B0 /* KGHitTestingHelper.m in Sources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXSourcesBuildPhase section */ 192 | 193 | /* Begin XCBuildConfiguration section */ 194 | 01B610D91AF5622B001BCC44 /* Debug */ = { 195 | isa = XCBuildConfiguration; 196 | buildSettings = { 197 | }; 198 | name = Debug; 199 | }; 200 | 01B610DA1AF5622B001BCC44 /* Release */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | }; 204 | name = Release; 205 | }; 206 | 01B610E71AF56269001BCC44 /* Debug */ = { 207 | isa = XCBuildConfiguration; 208 | buildSettings = { 209 | ALWAYS_SEARCH_USER_PATHS = NO; 210 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 211 | CLANG_CXX_LIBRARY = "libc++"; 212 | CLANG_ENABLE_MODULES = YES; 213 | CLANG_ENABLE_OBJC_ARC = YES; 214 | CLANG_WARN_BOOL_CONVERSION = YES; 215 | CLANG_WARN_CONSTANT_CONVERSION = YES; 216 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 217 | CLANG_WARN_EMPTY_BODY = YES; 218 | CLANG_WARN_ENUM_CONVERSION = YES; 219 | CLANG_WARN_INT_CONVERSION = YES; 220 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 221 | CLANG_WARN_UNREACHABLE_CODE = YES; 222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 223 | COPY_PHASE_STRIP = NO; 224 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 225 | ENABLE_STRICT_OBJC_MSGSEND = YES; 226 | FRAMEWORK_SEARCH_PATHS = ( 227 | "$(SDKROOT)/Developer/Library/Frameworks", 228 | "$(inherited)", 229 | ); 230 | GCC_C_LANGUAGE_STANDARD = gnu99; 231 | GCC_DYNAMIC_NO_PIC = NO; 232 | GCC_NO_COMMON_BLOCKS = YES; 233 | GCC_OPTIMIZATION_LEVEL = 0; 234 | GCC_PREPROCESSOR_DEFINITIONS = ( 235 | "DEBUG=1", 236 | "$(inherited)", 237 | ); 238 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 239 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 240 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 241 | GCC_WARN_UNDECLARED_SELECTOR = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 243 | GCC_WARN_UNUSED_FUNCTION = YES; 244 | GCC_WARN_UNUSED_VARIABLE = YES; 245 | INFOPLIST_FILE = KGHitTestingViewsTests/Info.plist; 246 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 247 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 248 | MTL_ENABLE_DEBUG_INFO = YES; 249 | ONLY_ACTIVE_ARCH = YES; 250 | PRODUCT_NAME = "$(TARGET_NAME)"; 251 | SDKROOT = iphoneos; 252 | }; 253 | name = Debug; 254 | }; 255 | 01B610E81AF56269001BCC44 /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | ALWAYS_SEARCH_USER_PATHS = NO; 259 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 260 | CLANG_CXX_LIBRARY = "libc++"; 261 | CLANG_ENABLE_MODULES = YES; 262 | CLANG_ENABLE_OBJC_ARC = YES; 263 | CLANG_WARN_BOOL_CONVERSION = YES; 264 | CLANG_WARN_CONSTANT_CONVERSION = YES; 265 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 266 | CLANG_WARN_EMPTY_BODY = YES; 267 | CLANG_WARN_ENUM_CONVERSION = YES; 268 | CLANG_WARN_INT_CONVERSION = YES; 269 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 270 | CLANG_WARN_UNREACHABLE_CODE = YES; 271 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 272 | COPY_PHASE_STRIP = NO; 273 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 274 | ENABLE_NS_ASSERTIONS = NO; 275 | ENABLE_STRICT_OBJC_MSGSEND = YES; 276 | FRAMEWORK_SEARCH_PATHS = ( 277 | "$(SDKROOT)/Developer/Library/Frameworks", 278 | "$(inherited)", 279 | ); 280 | GCC_C_LANGUAGE_STANDARD = gnu99; 281 | GCC_NO_COMMON_BLOCKS = YES; 282 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 283 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 284 | GCC_WARN_UNDECLARED_SELECTOR = YES; 285 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 286 | GCC_WARN_UNUSED_FUNCTION = YES; 287 | GCC_WARN_UNUSED_VARIABLE = YES; 288 | INFOPLIST_FILE = KGHitTestingViewsTests/Info.plist; 289 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 290 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 291 | MTL_ENABLE_DEBUG_INFO = NO; 292 | PRODUCT_NAME = "$(TARGET_NAME)"; 293 | SDKROOT = iphoneos; 294 | VALIDATE_PRODUCT = YES; 295 | }; 296 | name = Release; 297 | }; 298 | /* End XCBuildConfiguration section */ 299 | 300 | /* Begin XCConfigurationList section */ 301 | 01B610D81AF5622B001BCC44 /* Build configuration list for PBXProject "KGHitTestingViewsTests" */ = { 302 | isa = XCConfigurationList; 303 | buildConfigurations = ( 304 | 01B610D91AF5622B001BCC44 /* Debug */, 305 | 01B610DA1AF5622B001BCC44 /* Release */, 306 | ); 307 | defaultConfigurationIsVisible = 0; 308 | defaultConfigurationName = Release; 309 | }; 310 | 01B610E61AF56269001BCC44 /* Build configuration list for PBXNativeTarget "KGHitTestingViewsTests" */ = { 311 | isa = XCConfigurationList; 312 | buildConfigurations = ( 313 | 01B610E71AF56269001BCC44 /* Debug */, 314 | 01B610E81AF56269001BCC44 /* Release */, 315 | ); 316 | defaultConfigurationIsVisible = 0; 317 | defaultConfigurationName = Release; 318 | }; 319 | /* End XCConfigurationList section */ 320 | }; 321 | rootObject = 01B610D51AF5622B001BCC44 /* Project object */; 322 | } 323 | -------------------------------------------------------------------------------- /Example/KGHitTestingViewsExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0134810D1AF563560085731A /* KGHitTestingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0134810C1AF563560085731A /* KGHitTestingView.m */; }; 11 | 013481101AF5636F0085731A /* KGHitTestingButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 0134810F1AF5636F0085731A /* KGHitTestingButton.m */; }; 12 | 013481131AF564260085731A /* KGHitTestingHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 013481121AF564260085731A /* KGHitTestingHelper.m */; }; 13 | 01596BD71CFBD6AE006241B7 /* UIView+KGHitTesting.m in Sources */ = {isa = PBXBuildFile; fileRef = 01596BD61CFBD6AE006241B7 /* UIView+KGHitTesting.m */; }; 14 | 019A13EE1C41DEDB00AF7461 /* KGHitTestingControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 019A13ED1C41DEDB00AF7461 /* KGHitTestingControl.m */; }; 15 | 01B610B11AF561C6001BCC44 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B610B01AF561C6001BCC44 /* main.m */; }; 16 | 01B610B41AF561C6001BCC44 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B610B31AF561C6001BCC44 /* AppDelegate.m */; }; 17 | 01B610B71AF561C6001BCC44 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 01B610B61AF561C6001BCC44 /* ViewController.m */; }; 18 | 01B610BA1AF561C6001BCC44 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 01B610B81AF561C6001BCC44 /* Main.storyboard */; }; 19 | 01B610BC1AF561C6001BCC44 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 01B610BB1AF561C6001BCC44 /* Images.xcassets */; }; 20 | 01B610BF1AF561C6001BCC44 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 01B610BD1AF561C6001BCC44 /* LaunchScreen.xib */; }; 21 | /* End PBXBuildFile section */ 22 | 23 | /* Begin PBXFileReference section */ 24 | 0124079D1B0056F300B377D8 /* KGHitTesting.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = KGHitTesting.h; sourceTree = ""; }; 25 | 0134810B1AF563560085731A /* KGHitTestingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingView.h; sourceTree = ""; }; 26 | 0134810C1AF563560085731A /* KGHitTestingView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingView.m; sourceTree = ""; }; 27 | 0134810E1AF5636F0085731A /* KGHitTestingButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingButton.h; sourceTree = ""; }; 28 | 0134810F1AF5636F0085731A /* KGHitTestingButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingButton.m; sourceTree = ""; }; 29 | 013481111AF564260085731A /* KGHitTestingHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingHelper.h; sourceTree = ""; }; 30 | 013481121AF564260085731A /* KGHitTestingHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingHelper.m; sourceTree = ""; }; 31 | 01596BD51CFBD6AE006241B7 /* UIView+KGHitTesting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+KGHitTesting.h"; sourceTree = ""; }; 32 | 01596BD61CFBD6AE006241B7 /* UIView+KGHitTesting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+KGHitTesting.m"; sourceTree = ""; }; 33 | 019A13EC1C41DEDB00AF7461 /* KGHitTestingControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KGHitTestingControl.h; sourceTree = ""; }; 34 | 019A13ED1C41DEDB00AF7461 /* KGHitTestingControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KGHitTestingControl.m; sourceTree = ""; }; 35 | 01B610AB1AF561C6001BCC44 /* KGHitTestingViewsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KGHitTestingViewsExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 36 | 01B610AF1AF561C6001BCC44 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 37 | 01B610B01AF561C6001BCC44 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 38 | 01B610B21AF561C6001BCC44 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 39 | 01B610B31AF561C6001BCC44 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 40 | 01B610B51AF561C6001BCC44 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 41 | 01B610B61AF561C6001BCC44 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 42 | 01B610B91AF561C6001BCC44 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 43 | 01B610BB1AF561C6001BCC44 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 44 | 01B610BE1AF561C6001BCC44 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 01B610A81AF561C6001BCC44 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 0134810A1AF563210085731A /* KGHitTestingViews */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 0124079D1B0056F300B377D8 /* KGHitTesting.h */, 62 | 0134810B1AF563560085731A /* KGHitTestingView.h */, 63 | 0134810C1AF563560085731A /* KGHitTestingView.m */, 64 | 0134810E1AF5636F0085731A /* KGHitTestingButton.h */, 65 | 0134810F1AF5636F0085731A /* KGHitTestingButton.m */, 66 | 019A13EC1C41DEDB00AF7461 /* KGHitTestingControl.h */, 67 | 019A13ED1C41DEDB00AF7461 /* KGHitTestingControl.m */, 68 | 013481111AF564260085731A /* KGHitTestingHelper.h */, 69 | 013481121AF564260085731A /* KGHitTestingHelper.m */, 70 | 01596BD51CFBD6AE006241B7 /* UIView+KGHitTesting.h */, 71 | 01596BD61CFBD6AE006241B7 /* UIView+KGHitTesting.m */, 72 | ); 73 | name = KGHitTestingViews; 74 | path = ../../KGHitTestingViews; 75 | sourceTree = ""; 76 | }; 77 | 01B610A21AF561C6001BCC44 = { 78 | isa = PBXGroup; 79 | children = ( 80 | 01B610AD1AF561C6001BCC44 /* KGHitTestingViewsExample */, 81 | 01B610AC1AF561C6001BCC44 /* Products */, 82 | ); 83 | sourceTree = ""; 84 | }; 85 | 01B610AC1AF561C6001BCC44 /* Products */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 01B610AB1AF561C6001BCC44 /* KGHitTestingViewsExample.app */, 89 | ); 90 | name = Products; 91 | sourceTree = ""; 92 | }; 93 | 01B610AD1AF561C6001BCC44 /* KGHitTestingViewsExample */ = { 94 | isa = PBXGroup; 95 | children = ( 96 | 01B610B21AF561C6001BCC44 /* AppDelegate.h */, 97 | 01B610B31AF561C6001BCC44 /* AppDelegate.m */, 98 | 01B610B81AF561C6001BCC44 /* Main.storyboard */, 99 | 01B610B51AF561C6001BCC44 /* ViewController.h */, 100 | 01B610B61AF561C6001BCC44 /* ViewController.m */, 101 | 01B610BB1AF561C6001BCC44 /* Images.xcassets */, 102 | 01B610BD1AF561C6001BCC44 /* LaunchScreen.xib */, 103 | 0134810A1AF563210085731A /* KGHitTestingViews */, 104 | 01B610AE1AF561C6001BCC44 /* Supporting Files */, 105 | ); 106 | path = KGHitTestingViewsExample; 107 | sourceTree = ""; 108 | }; 109 | 01B610AE1AF561C6001BCC44 /* Supporting Files */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 01B610AF1AF561C6001BCC44 /* Info.plist */, 113 | 01B610B01AF561C6001BCC44 /* main.m */, 114 | ); 115 | name = "Supporting Files"; 116 | sourceTree = ""; 117 | }; 118 | /* End PBXGroup section */ 119 | 120 | /* Begin PBXNativeTarget section */ 121 | 01B610AA1AF561C6001BCC44 /* KGHitTestingViewsExample */ = { 122 | isa = PBXNativeTarget; 123 | buildConfigurationList = 01B610CE1AF561C6001BCC44 /* Build configuration list for PBXNativeTarget "KGHitTestingViewsExample" */; 124 | buildPhases = ( 125 | 01B610A71AF561C6001BCC44 /* Sources */, 126 | 01B610A81AF561C6001BCC44 /* Frameworks */, 127 | 01B610A91AF561C6001BCC44 /* Resources */, 128 | ); 129 | buildRules = ( 130 | ); 131 | dependencies = ( 132 | ); 133 | name = KGHitTestingViewsExample; 134 | productName = KGHitTestingViewsExample; 135 | productReference = 01B610AB1AF561C6001BCC44 /* KGHitTestingViewsExample.app */; 136 | productType = "com.apple.product-type.application"; 137 | }; 138 | /* End PBXNativeTarget section */ 139 | 140 | /* Begin PBXProject section */ 141 | 01B610A31AF561C6001BCC44 /* Project object */ = { 142 | isa = PBXProject; 143 | attributes = { 144 | LastUpgradeCheck = 0630; 145 | ORGANIZATIONNAME = "Krisjanis Gaidis"; 146 | TargetAttributes = { 147 | 01B610AA1AF561C6001BCC44 = { 148 | CreatedOnToolsVersion = 6.3.1; 149 | }; 150 | }; 151 | }; 152 | buildConfigurationList = 01B610A61AF561C6001BCC44 /* Build configuration list for PBXProject "KGHitTestingViewsExample" */; 153 | compatibilityVersion = "Xcode 3.2"; 154 | developmentRegion = English; 155 | hasScannedForEncodings = 0; 156 | knownRegions = ( 157 | en, 158 | Base, 159 | ); 160 | mainGroup = 01B610A21AF561C6001BCC44; 161 | productRefGroup = 01B610AC1AF561C6001BCC44 /* Products */; 162 | projectDirPath = ""; 163 | projectRoot = ""; 164 | targets = ( 165 | 01B610AA1AF561C6001BCC44 /* KGHitTestingViewsExample */, 166 | ); 167 | }; 168 | /* End PBXProject section */ 169 | 170 | /* Begin PBXResourcesBuildPhase section */ 171 | 01B610A91AF561C6001BCC44 /* Resources */ = { 172 | isa = PBXResourcesBuildPhase; 173 | buildActionMask = 2147483647; 174 | files = ( 175 | 01B610BA1AF561C6001BCC44 /* Main.storyboard in Resources */, 176 | 01B610BF1AF561C6001BCC44 /* LaunchScreen.xib in Resources */, 177 | 01B610BC1AF561C6001BCC44 /* Images.xcassets in Resources */, 178 | ); 179 | runOnlyForDeploymentPostprocessing = 0; 180 | }; 181 | /* End PBXResourcesBuildPhase section */ 182 | 183 | /* Begin PBXSourcesBuildPhase section */ 184 | 01B610A71AF561C6001BCC44 /* Sources */ = { 185 | isa = PBXSourcesBuildPhase; 186 | buildActionMask = 2147483647; 187 | files = ( 188 | 01B610B71AF561C6001BCC44 /* ViewController.m in Sources */, 189 | 01596BD71CFBD6AE006241B7 /* UIView+KGHitTesting.m in Sources */, 190 | 019A13EE1C41DEDB00AF7461 /* KGHitTestingControl.m in Sources */, 191 | 0134810D1AF563560085731A /* KGHitTestingView.m in Sources */, 192 | 013481131AF564260085731A /* KGHitTestingHelper.m in Sources */, 193 | 01B610B41AF561C6001BCC44 /* AppDelegate.m in Sources */, 194 | 01B610B11AF561C6001BCC44 /* main.m in Sources */, 195 | 013481101AF5636F0085731A /* KGHitTestingButton.m in Sources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXSourcesBuildPhase section */ 200 | 201 | /* Begin PBXVariantGroup section */ 202 | 01B610B81AF561C6001BCC44 /* Main.storyboard */ = { 203 | isa = PBXVariantGroup; 204 | children = ( 205 | 01B610B91AF561C6001BCC44 /* Base */, 206 | ); 207 | name = Main.storyboard; 208 | sourceTree = ""; 209 | }; 210 | 01B610BD1AF561C6001BCC44 /* LaunchScreen.xib */ = { 211 | isa = PBXVariantGroup; 212 | children = ( 213 | 01B610BE1AF561C6001BCC44 /* Base */, 214 | ); 215 | name = LaunchScreen.xib; 216 | sourceTree = ""; 217 | }; 218 | /* End PBXVariantGroup section */ 219 | 220 | /* Begin XCBuildConfiguration section */ 221 | 01B610CC1AF561C6001BCC44 /* Debug */ = { 222 | isa = XCBuildConfiguration; 223 | buildSettings = { 224 | ALWAYS_SEARCH_USER_PATHS = NO; 225 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 226 | CLANG_CXX_LIBRARY = "libc++"; 227 | CLANG_ENABLE_MODULES = YES; 228 | CLANG_ENABLE_OBJC_ARC = YES; 229 | CLANG_WARN_BOOL_CONVERSION = YES; 230 | CLANG_WARN_CONSTANT_CONVERSION = YES; 231 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 232 | CLANG_WARN_EMPTY_BODY = YES; 233 | CLANG_WARN_ENUM_CONVERSION = YES; 234 | CLANG_WARN_INT_CONVERSION = YES; 235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 236 | CLANG_WARN_UNREACHABLE_CODE = YES; 237 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 238 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 239 | COPY_PHASE_STRIP = NO; 240 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 241 | ENABLE_STRICT_OBJC_MSGSEND = YES; 242 | GCC_C_LANGUAGE_STANDARD = gnu99; 243 | GCC_DYNAMIC_NO_PIC = NO; 244 | GCC_NO_COMMON_BLOCKS = YES; 245 | GCC_OPTIMIZATION_LEVEL = 0; 246 | GCC_PREPROCESSOR_DEFINITIONS = ( 247 | "DEBUG=1", 248 | "$(inherited)", 249 | ); 250 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 251 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 252 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 253 | GCC_WARN_UNDECLARED_SELECTOR = YES; 254 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 255 | GCC_WARN_UNUSED_FUNCTION = YES; 256 | GCC_WARN_UNUSED_VARIABLE = YES; 257 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 258 | MTL_ENABLE_DEBUG_INFO = YES; 259 | ONLY_ACTIVE_ARCH = YES; 260 | SDKROOT = iphoneos; 261 | }; 262 | name = Debug; 263 | }; 264 | 01B610CD1AF561C6001BCC44 /* Release */ = { 265 | isa = XCBuildConfiguration; 266 | buildSettings = { 267 | ALWAYS_SEARCH_USER_PATHS = NO; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_MODULES = YES; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INT_CONVERSION = YES; 278 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 279 | CLANG_WARN_UNREACHABLE_CODE = YES; 280 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 281 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 282 | COPY_PHASE_STRIP = NO; 283 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 284 | ENABLE_NS_ASSERTIONS = NO; 285 | ENABLE_STRICT_OBJC_MSGSEND = YES; 286 | GCC_C_LANGUAGE_STANDARD = gnu99; 287 | GCC_NO_COMMON_BLOCKS = YES; 288 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 289 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 290 | GCC_WARN_UNDECLARED_SELECTOR = YES; 291 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 292 | GCC_WARN_UNUSED_FUNCTION = YES; 293 | GCC_WARN_UNUSED_VARIABLE = YES; 294 | IPHONEOS_DEPLOYMENT_TARGET = 8.3; 295 | MTL_ENABLE_DEBUG_INFO = NO; 296 | SDKROOT = iphoneos; 297 | VALIDATE_PRODUCT = YES; 298 | }; 299 | name = Release; 300 | }; 301 | 01B610CF1AF561C6001BCC44 /* Debug */ = { 302 | isa = XCBuildConfiguration; 303 | buildSettings = { 304 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 305 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 306 | INFOPLIST_FILE = KGHitTestingViewsExample/Info.plist; 307 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 308 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | }; 311 | name = Debug; 312 | }; 313 | 01B610D01AF561C6001BCC44 /* Release */ = { 314 | isa = XCBuildConfiguration; 315 | buildSettings = { 316 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 317 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 318 | INFOPLIST_FILE = KGHitTestingViewsExample/Info.plist; 319 | IPHONEOS_DEPLOYMENT_TARGET = 6.0; 320 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 321 | PRODUCT_NAME = "$(TARGET_NAME)"; 322 | }; 323 | name = Release; 324 | }; 325 | /* End XCBuildConfiguration section */ 326 | 327 | /* Begin XCConfigurationList section */ 328 | 01B610A61AF561C6001BCC44 /* Build configuration list for PBXProject "KGHitTestingViewsExample" */ = { 329 | isa = XCConfigurationList; 330 | buildConfigurations = ( 331 | 01B610CC1AF561C6001BCC44 /* Debug */, 332 | 01B610CD1AF561C6001BCC44 /* Release */, 333 | ); 334 | defaultConfigurationIsVisible = 0; 335 | defaultConfigurationName = Release; 336 | }; 337 | 01B610CE1AF561C6001BCC44 /* Build configuration list for PBXNativeTarget "KGHitTestingViewsExample" */ = { 338 | isa = XCConfigurationList; 339 | buildConfigurations = ( 340 | 01B610CF1AF561C6001BCC44 /* Debug */, 341 | 01B610D01AF561C6001BCC44 /* Release */, 342 | ); 343 | defaultConfigurationIsVisible = 0; 344 | defaultConfigurationName = Release; 345 | }; 346 | /* End XCConfigurationList section */ 347 | }; 348 | rootObject = 01B610A31AF561C6001BCC44 /* Project object */; 349 | } 350 | --------------------------------------------------------------------------------