├── CKShapeView.podspec ├── CKShapeView ├── CKShapeView.h └── CKShapeView.m ├── Demo ├── CKAppDelegate.h ├── CKAppDelegate.m ├── CKViewController.h ├── CKViewController.m ├── Default-568h@2x.png ├── Demo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Info.plist └── main.m ├── LICENSE └── README.md /CKShapeView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'CKShapeView' 3 | s.version = '0.1.3' 4 | s.license = 'MIT' 5 | s.summary = 'UIView subclass that is backed by CAShapeLayer' 6 | s.homepage = 'https://github.com/conradev/CKShapeView' 7 | s.author = { 'Conrad Kramer' => 'conrad@kramerapps.com' } 8 | s.source = { :git => 'https://github.com/conradev/CKShapeView.git', 9 | :tag => '0.1.3' } 10 | s.source_files = 'CKShapeView' 11 | s.requires_arc = true 12 | s.platform = :ios, '4.3' 13 | s.frameworks = 'UIKit', 'QuartzCore' 14 | end 15 | -------------------------------------------------------------------------------- /CKShapeView/CKShapeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CKShapeView.h 3 | // CKShapeView 4 | // 5 | // Created by Conrad Kramer on 8/19/13. 6 | // Copyright (c) 2013 Kramer Software Productions, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CKShapeView : UIView 12 | 13 | @property BOOL hitTestUsingPath; 14 | 15 | @property (copy) UIBezierPath *path; 16 | 17 | @property UIColor *fillColor; 18 | 19 | @property (copy) NSString *fillRule; 20 | 21 | @property UIColor *strokeColor; 22 | 23 | @property CGFloat strokeStart, strokeEnd; 24 | 25 | @property CGFloat lineWidth; 26 | 27 | @property CGFloat miterLimit; 28 | 29 | @property (copy) NSString *lineCap; 30 | 31 | @property (copy) NSString *lineJoin; 32 | 33 | @property CGFloat lineDashPhase; 34 | 35 | @property (copy) NSArray *lineDashPattern; 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CKShapeView/CKShapeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CKShapeView.m 3 | // CKShapeView 4 | // 5 | // Created by Conrad Kramer on 8/19/13. 6 | // Copyright (c) 2013 Kramer Software Productions, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "CKShapeView.h" 12 | 13 | //@interface UIView (Private) 14 | // 15 | //- (BOOL)_shouldAnimatePropertyWithKey:(NSString *)key; 16 | // 17 | //@end 18 | 19 | @interface CKShapeView () 20 | 21 | @property (nonatomic, readonly, retain) CAShapeLayer *layer; 22 | 23 | @end 24 | 25 | @implementation CKShapeView 26 | 27 | @dynamic layer; 28 | 29 | @dynamic path, fillColor, fillRule, strokeColor, strokeStart, strokeEnd, lineWidth, miterLimit, lineCap, lineJoin, lineDashPhase, lineDashPattern; 30 | 31 | + (Class)layerClass { 32 | return [CAShapeLayer class]; 33 | } 34 | 35 | - (BOOL)shouldForwardSelector:(SEL)aSelector { 36 | return (![[self.layer superclass] instancesRespondToSelector:aSelector] && 37 | [self.layer respondsToSelector:aSelector]); 38 | } 39 | 40 | - (id)forwardingTargetForSelector:(SEL)aSelector { 41 | return (![self respondsToSelector:aSelector] && [self shouldForwardSelector:aSelector]) ? self.layer : self; 42 | } 43 | 44 | - (void)setPath:(UIBezierPath *)path { 45 | self.layer.path = path.CGPath; 46 | self.layer.fillRule = path.usesEvenOddFillRule ? kCAFillRuleEvenOdd : kCAFillRuleNonZero; 47 | self.layer.lineWidth = path.lineWidth; 48 | self.layer.miterLimit = path.miterLimit; 49 | 50 | switch (path.lineCapStyle) { 51 | case kCGLineCapButt: 52 | self.layer.lineCap = kCALineCapButt; 53 | break; 54 | case kCGLineCapRound: 55 | self.layer.lineCap = kCALineCapRound; 56 | break; 57 | case kCGLineCapSquare: 58 | self.layer.lineCap = kCALineCapSquare; 59 | break; 60 | } 61 | 62 | switch (path.lineJoinStyle) { 63 | case kCGLineJoinMiter: 64 | self.layer.lineJoin = kCALineJoinMiter; 65 | break; 66 | case kCGLineJoinRound: 67 | self.layer.lineJoin = kCALineJoinRound; 68 | break; 69 | case kCGLineJoinBevel: 70 | self.layer.lineJoin = kCALineJoinBevel; 71 | break; 72 | } 73 | 74 | NSInteger count; 75 | [path getLineDash:NULL count:&count phase:NULL]; 76 | CGFloat pattern[count], phase; 77 | [path getLineDash:pattern count:NULL phase:&phase]; 78 | 79 | NSMutableArray *lineDashPattern = [NSMutableArray array]; 80 | for (NSUInteger i = 0; i < count; i++) { 81 | [lineDashPattern addObject:@(pattern[i])]; 82 | } 83 | 84 | self.layer.lineDashPattern = [lineDashPattern copy]; 85 | self.layer.lineDashPhase = phase; 86 | } 87 | 88 | - (UIBezierPath *)path { 89 | UIBezierPath *path = [UIBezierPath bezierPathWithCGPath:self.layer.path]; 90 | path.lineWidth = self.layer.lineWidth; 91 | path.usesEvenOddFillRule = (self.layer.fillRule == kCAFillRuleEvenOdd); 92 | path.lineWidth = self.layer.lineWidth; 93 | path.miterLimit = self.layer.miterLimit; 94 | 95 | NSString *lineCap = self.layer.lineCap; 96 | if ([lineCap isEqualToString:kCALineCapButt]) { 97 | path.lineCapStyle = kCGLineCapButt; 98 | } else if ([lineCap isEqualToString:kCALineCapRound]) { 99 | path.lineCapStyle = kCGLineCapRound; 100 | } else if ([lineCap isEqualToString:kCALineCapSquare]) { 101 | path.lineCapStyle = kCGLineCapSquare; 102 | } 103 | 104 | NSString *lineJoin = self.layer.lineJoin; 105 | if ([lineJoin isEqualToString:kCALineJoinMiter]) { 106 | path.lineJoinStyle = kCGLineJoinMiter; 107 | } else if ([lineJoin isEqualToString:kCALineJoinRound]) { 108 | path.lineJoinStyle = kCGLineJoinRound; 109 | } else if ([lineJoin isEqualToString:kCALineJoinBevel]) { 110 | path.lineJoinStyle = kCGLineJoinBevel; 111 | } 112 | 113 | CGFloat phase = self.layer.lineDashPhase; 114 | NSInteger count = self.layer.lineDashPattern.count; 115 | CGFloat pattern[count]; 116 | for (NSUInteger i = 0; i < count; i++) { 117 | pattern[i] = [[self.layer.lineDashPattern objectAtIndex:i] floatValue]; 118 | } 119 | 120 | [path setLineDash:pattern count:count phase:phase]; 121 | 122 | return path; 123 | } 124 | 125 | - (void)setFillColor:(UIColor *)fillColor { 126 | self.layer.fillColor = fillColor.CGColor; 127 | } 128 | 129 | - (UIColor *)fillColor { 130 | return [UIColor colorWithCGColor:self.layer.fillColor]; 131 | } 132 | 133 | - (void)setStrokeColor:(UIColor *)strokeColor { 134 | self.layer.strokeColor = strokeColor.CGColor; 135 | } 136 | 137 | - (UIColor *)strokeColor { 138 | return [UIColor colorWithCGColor:self.layer.strokeColor]; 139 | } 140 | 141 | - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event { 142 | BOOL inside = [super pointInside:point withEvent:event]; 143 | return self.hitTestUsingPath ? [self.path containsPoint:point] && inside : inside; 144 | } 145 | 146 | // This is a cleaner way of enabling animation, but it involves using a private API :( 147 | 148 | //- (BOOL)_shouldAnimatePropertyWithKey:(NSString *)key { 149 | // return ([self shouldForwardSelector:NSSelectorFromString(key)] || [super _shouldAnimatePropertyWithKey:key]); 150 | //} 151 | 152 | - (id)actionForLayer:(CALayer *)layer forKey:(NSString *)key { 153 | id action = [super actionForLayer:layer forKey:key]; 154 | 155 | if ([layer isEqual:self.layer] && [[NSNull null] isEqual:action]) { 156 | if ([self shouldForwardSelector:NSSelectorFromString(key)]) { 157 | CABasicAnimation *animation = (CABasicAnimation *)[self actionForLayer:layer forKey:@"backgroundColor"]; 158 | if ([animation isKindOfClass:[CABasicAnimation class]]) { 159 | animation.fromValue = [layer valueForKey:key]; 160 | animation.keyPath = key; 161 | action = animation; 162 | } 163 | } 164 | } 165 | 166 | return action; 167 | } 168 | 169 | @end 170 | -------------------------------------------------------------------------------- /Demo/CKAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CKAppDelegate.h 3 | // Demo 4 | // 5 | // Created by Conrad Kramer on 1/20/14. 6 | // Copyright (c) 2014 Conrad Kramer. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | @interface CKAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Demo/CKAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CKAppDelegate.m 3 | // Demo 4 | // 5 | // Created by Conrad Kramer on 1/20/14. 6 | // Copyright (c) 2014 Conrad Kramer. All rights reserved. 7 | // 8 | 9 | #import "CKAppDelegate.h" 10 | #import "CKViewController.h" 11 | 12 | @implementation CKAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 15 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 16 | self.window.rootViewController = [[CKViewController alloc] init]; 17 | [self.window makeKeyAndVisible]; 18 | 19 | return YES; 20 | } 21 | 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /Demo/CKViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CKViewController.h 3 | // Demo 4 | // 5 | // Created by Conrad Kramer on 1/20/14. 6 | // Copyright (c) 2014 Conrad Kramer. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CKViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Demo/CKViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CKViewController.m 3 | // Demo 4 | // 5 | // Created by Conrad Kramer on 1/20/14. 6 | // Copyright (c) 2014 Conrad Kramer. All rights reserved. 7 | // 8 | 9 | #import "CKViewController.h" 10 | #import "CKShapeView.h" 11 | 12 | @interface CKViewController () 13 | 14 | @property (weak, nonatomic) CKShapeView *pieView; 15 | 16 | @end 17 | 18 | @implementation CKViewController 19 | 20 | - (void)loadView { 21 | [super loadView]; 22 | 23 | self.view.backgroundColor = [UIColor whiteColor]; 24 | 25 | CKShapeView *pieView = [[CKShapeView alloc] init]; 26 | pieView.fillColor = nil; 27 | pieView.strokeColor = [UIColor blackColor]; 28 | pieView.translatesAutoresizingMaskIntoConstraints = NO; 29 | [self.view addSubview:pieView]; 30 | self.pieView = pieView; 31 | 32 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:pieView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:pieView attribute:NSLayoutAttributeWidth multiplier:1.0f constant:0]]; 33 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:pieView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0f constant:250]]; 34 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:pieView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0]]; 35 | [self.view addConstraint:[NSLayoutConstraint constraintWithItem:pieView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0]]; 36 | } 37 | 38 | - (void)viewDidLoad { 39 | UIViewAnimationOptions options = UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat; 40 | [UIView animateWithDuration:1.0f delay:0.0f options:options animations:^{ 41 | self.pieView.strokeEnd = 0.0f; 42 | } completion:nil]; 43 | } 44 | 45 | - (void)viewDidLayoutSubviews { 46 | CGFloat width = CGRectGetWidth(self.pieView.bounds); 47 | self.pieView.path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(self.pieView.bounds, width/4, width/4)]; 48 | self.pieView.lineWidth = width/2; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Demo/Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conradev/CKShapeView/cfedfab5bbc67a390b6b6e70f803676da902d556/Demo/Default-568h@2x.png -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | D01394CC188DE401003861F8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01394CB188DE401003861F8 /* Foundation.framework */; }; 11 | D01394CE188DE401003861F8 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01394CD188DE401003861F8 /* CoreGraphics.framework */; }; 12 | D01394D0188DE401003861F8 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D01394CF188DE401003861F8 /* UIKit.framework */; }; 13 | D01394DC188DE401003861F8 /* CKAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = D01394DB188DE401003861F8 /* CKAppDelegate.m */; }; 14 | D01394E2188DE401003861F8 /* CKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D01394E1188DE401003861F8 /* CKViewController.m */; }; 15 | D0139503188DE4AD003861F8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D0139501188DE4AD003861F8 /* main.m */; }; 16 | D0139507188DE560003861F8 /* CKShapeView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0139506188DE560003861F8 /* CKShapeView.m */; }; 17 | D0139509188DE6E4003861F8 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = D0139508188DE6E4003861F8 /* Default-568h@2x.png */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | D01394C8188DE401003861F8 /* ShapeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShapeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 22 | D01394CB188DE401003861F8 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 23 | D01394CD188DE401003861F8 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 24 | D01394CF188DE401003861F8 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 25 | D01394DA188DE401003861F8 /* CKAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKAppDelegate.h; sourceTree = SOURCE_ROOT; }; 26 | D01394DB188DE401003861F8 /* CKAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKAppDelegate.m; sourceTree = SOURCE_ROOT; }; 27 | D01394E0188DE401003861F8 /* CKViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CKViewController.h; sourceTree = SOURCE_ROOT; }; 28 | D01394E1188DE401003861F8 /* CKViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CKViewController.m; sourceTree = SOURCE_ROOT; }; 29 | D01394EA188DE402003861F8 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 30 | D0139500188DE4AD003861F8 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; 31 | D0139501188DE4AD003861F8 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = SOURCE_ROOT; }; 32 | D0139505188DE560003861F8 /* CKShapeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKShapeView.h; sourceTree = ""; }; 33 | D0139506188DE560003861F8 /* CKShapeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CKShapeView.m; sourceTree = ""; }; 34 | D0139508188DE6E4003861F8 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = SOURCE_ROOT; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | D01394C5188DE401003861F8 /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | D01394CE188DE401003861F8 /* CoreGraphics.framework in Frameworks */, 43 | D01394D0188DE401003861F8 /* UIKit.framework in Frameworks */, 44 | D01394CC188DE401003861F8 /* Foundation.framework in Frameworks */, 45 | ); 46 | runOnlyForDeploymentPostprocessing = 0; 47 | }; 48 | /* End PBXFrameworksBuildPhase section */ 49 | 50 | /* Begin PBXGroup section */ 51 | D01394BF188DE401003861F8 = { 52 | isa = PBXGroup; 53 | children = ( 54 | D01394D1188DE401003861F8 /* Demo */, 55 | D0139504188DE560003861F8 /* CKShapeView */, 56 | D01394CA188DE401003861F8 /* Frameworks */, 57 | D01394C9188DE401003861F8 /* Products */, 58 | ); 59 | sourceTree = ""; 60 | }; 61 | D01394C9188DE401003861F8 /* Products */ = { 62 | isa = PBXGroup; 63 | children = ( 64 | D01394C8188DE401003861F8 /* ShapeDemo.app */, 65 | ); 66 | name = Products; 67 | sourceTree = ""; 68 | }; 69 | D01394CA188DE401003861F8 /* Frameworks */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | D01394CB188DE401003861F8 /* Foundation.framework */, 73 | D01394CD188DE401003861F8 /* CoreGraphics.framework */, 74 | D01394CF188DE401003861F8 /* UIKit.framework */, 75 | D01394EA188DE402003861F8 /* XCTest.framework */, 76 | ); 77 | name = Frameworks; 78 | sourceTree = ""; 79 | }; 80 | D01394D1188DE401003861F8 /* Demo */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | D01394DA188DE401003861F8 /* CKAppDelegate.h */, 84 | D01394DB188DE401003861F8 /* CKAppDelegate.m */, 85 | D01394E0188DE401003861F8 /* CKViewController.h */, 86 | D01394E1188DE401003861F8 /* CKViewController.m */, 87 | D0139501188DE4AD003861F8 /* main.m */, 88 | D0139500188DE4AD003861F8 /* Info.plist */, 89 | D0139508188DE6E4003861F8 /* Default-568h@2x.png */, 90 | ); 91 | path = Demo; 92 | sourceTree = ""; 93 | }; 94 | D0139504188DE560003861F8 /* CKShapeView */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | D0139505188DE560003861F8 /* CKShapeView.h */, 98 | D0139506188DE560003861F8 /* CKShapeView.m */, 99 | ); 100 | name = CKShapeView; 101 | path = ../CKShapeView; 102 | sourceTree = SOURCE_ROOT; 103 | }; 104 | /* End PBXGroup section */ 105 | 106 | /* Begin PBXNativeTarget section */ 107 | D01394C7188DE401003861F8 /* ShapeDemo */ = { 108 | isa = PBXNativeTarget; 109 | buildConfigurationList = D01394FA188DE402003861F8 /* Build configuration list for PBXNativeTarget "ShapeDemo" */; 110 | buildPhases = ( 111 | D01394C4188DE401003861F8 /* Sources */, 112 | D01394C5188DE401003861F8 /* Frameworks */, 113 | D01394C6188DE401003861F8 /* Resources */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = ShapeDemo; 120 | productName = Demo; 121 | productReference = D01394C8188DE401003861F8 /* ShapeDemo.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | D01394C0188DE401003861F8 /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | CLASSPREFIX = CK; 131 | LastUpgradeCheck = 0500; 132 | ORGANIZATIONNAME = "Conrad Kramer"; 133 | }; 134 | buildConfigurationList = D01394C3188DE401003861F8 /* Build configuration list for PBXProject "Demo" */; 135 | compatibilityVersion = "Xcode 3.2"; 136 | developmentRegion = English; 137 | hasScannedForEncodings = 0; 138 | knownRegions = ( 139 | en, 140 | Base, 141 | ); 142 | mainGroup = D01394BF188DE401003861F8; 143 | productRefGroup = D01394C9188DE401003861F8 /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | D01394C7188DE401003861F8 /* ShapeDemo */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXResourcesBuildPhase section */ 153 | D01394C6188DE401003861F8 /* Resources */ = { 154 | isa = PBXResourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | D0139509188DE6E4003861F8 /* Default-568h@2x.png in Resources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXResourcesBuildPhase section */ 162 | 163 | /* Begin PBXSourcesBuildPhase section */ 164 | D01394C4188DE401003861F8 /* Sources */ = { 165 | isa = PBXSourcesBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | D01394DC188DE401003861F8 /* CKAppDelegate.m in Sources */, 169 | D0139503188DE4AD003861F8 /* main.m in Sources */, 170 | D0139507188DE560003861F8 /* CKShapeView.m in Sources */, 171 | D01394E2188DE401003861F8 /* CKViewController.m in Sources */, 172 | ); 173 | runOnlyForDeploymentPostprocessing = 0; 174 | }; 175 | /* End PBXSourcesBuildPhase section */ 176 | 177 | /* Begin XCBuildConfiguration section */ 178 | D01394F8188DE402003861F8 /* Debug */ = { 179 | isa = XCBuildConfiguration; 180 | buildSettings = { 181 | ALWAYS_SEARCH_USER_PATHS = NO; 182 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 183 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 184 | CLANG_CXX_LIBRARY = "libc++"; 185 | CLANG_ENABLE_MODULES = YES; 186 | CLANG_ENABLE_OBJC_ARC = YES; 187 | CLANG_WARN_BOOL_CONVERSION = YES; 188 | CLANG_WARN_CONSTANT_CONVERSION = YES; 189 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 190 | CLANG_WARN_EMPTY_BODY = YES; 191 | CLANG_WARN_ENUM_CONVERSION = YES; 192 | CLANG_WARN_INT_CONVERSION = YES; 193 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 194 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 195 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 196 | COPY_PHASE_STRIP = NO; 197 | GCC_C_LANGUAGE_STANDARD = gnu99; 198 | GCC_DYNAMIC_NO_PIC = NO; 199 | GCC_OPTIMIZATION_LEVEL = 0; 200 | GCC_PREPROCESSOR_DEFINITIONS = ( 201 | "DEBUG=1", 202 | "$(inherited)", 203 | ); 204 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 205 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 206 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 207 | GCC_WARN_UNDECLARED_SELECTOR = YES; 208 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 209 | GCC_WARN_UNUSED_FUNCTION = YES; 210 | GCC_WARN_UNUSED_VARIABLE = YES; 211 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 212 | ONLY_ACTIVE_ARCH = YES; 213 | SDKROOT = iphoneos; 214 | }; 215 | name = Debug; 216 | }; 217 | D01394F9188DE402003861F8 /* Release */ = { 218 | isa = XCBuildConfiguration; 219 | buildSettings = { 220 | ALWAYS_SEARCH_USER_PATHS = NO; 221 | ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)"; 222 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 223 | CLANG_CXX_LIBRARY = "libc++"; 224 | CLANG_ENABLE_MODULES = YES; 225 | CLANG_ENABLE_OBJC_ARC = YES; 226 | CLANG_WARN_BOOL_CONVERSION = YES; 227 | CLANG_WARN_CONSTANT_CONVERSION = YES; 228 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 229 | CLANG_WARN_EMPTY_BODY = YES; 230 | CLANG_WARN_ENUM_CONVERSION = YES; 231 | CLANG_WARN_INT_CONVERSION = YES; 232 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 235 | COPY_PHASE_STRIP = YES; 236 | ENABLE_NS_ASSERTIONS = NO; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 240 | GCC_WARN_UNDECLARED_SELECTOR = YES; 241 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 242 | GCC_WARN_UNUSED_FUNCTION = YES; 243 | GCC_WARN_UNUSED_VARIABLE = YES; 244 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 245 | SDKROOT = iphoneos; 246 | VALIDATE_PRODUCT = YES; 247 | }; 248 | name = Release; 249 | }; 250 | D01394FB188DE402003861F8 /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 254 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 255 | INFOPLIST_FILE = Info.plist; 256 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 257 | PRODUCT_NAME = "$(TARGET_NAME)"; 258 | TARGETED_DEVICE_FAMILY = "1,2"; 259 | WRAPPER_EXTENSION = app; 260 | }; 261 | name = Debug; 262 | }; 263 | D01394FC188DE402003861F8 /* Release */ = { 264 | isa = XCBuildConfiguration; 265 | buildSettings = { 266 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 267 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 268 | INFOPLIST_FILE = Info.plist; 269 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 270 | PRODUCT_NAME = "$(TARGET_NAME)"; 271 | TARGETED_DEVICE_FAMILY = "1,2"; 272 | WRAPPER_EXTENSION = app; 273 | }; 274 | name = Release; 275 | }; 276 | /* End XCBuildConfiguration section */ 277 | 278 | /* Begin XCConfigurationList section */ 279 | D01394C3188DE401003861F8 /* Build configuration list for PBXProject "Demo" */ = { 280 | isa = XCConfigurationList; 281 | buildConfigurations = ( 282 | D01394F8188DE402003861F8 /* Debug */, 283 | D01394F9188DE402003861F8 /* Release */, 284 | ); 285 | defaultConfigurationIsVisible = 0; 286 | defaultConfigurationName = Release; 287 | }; 288 | D01394FA188DE402003861F8 /* Build configuration list for PBXNativeTarget "ShapeDemo" */ = { 289 | isa = XCConfigurationList; 290 | buildConfigurations = ( 291 | D01394FB188DE402003861F8 /* Debug */, 292 | D01394FC188DE402003861F8 /* Release */, 293 | ); 294 | defaultConfigurationIsVisible = 0; 295 | defaultConfigurationName = Release; 296 | }; 297 | /* End XCConfigurationList section */ 298 | }; 299 | rootObject = D01394C0188DE401003861F8 /* Project object */; 300 | } 301 | -------------------------------------------------------------------------------- /Demo/Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Demo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.kramerapps.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Demo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // Demo 4 | // 5 | // Created by Conrad Kramer on 1/20/14. 6 | // Copyright (c) 2014 Conrad Kramer. All rights reserved. 7 | // 8 | 9 | @import UIKit; 10 | 11 | #import "CKAppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) { 14 | @autoreleasepool { 15 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([CKAppDelegate class])); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Kramer Software Productions, LLC. (http://kramerapps.com/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CKShapeView 2 | 3 | CKShapeView is a `UIView` subclass that is backed by a `CAShapeLayer`. 4 | 5 | In other words, it is a view that is capable of rendering an arbitrary `CGPath`. 6 | 7 | It is completely configurable **and animatable**, so you can have custom drawn views without needing to subclass. 8 | 9 | `CKShapeView` has all of the properties of `CAShapeLayer`, with the addition of a `hitTestUsingPath` property that allows you to hit test using the path instead of the view's bounds. 10 | 11 | ## Example Usage 12 | 13 | ``` objc 14 | CKShapeView *pieView = [[CKShapeView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; 15 | CGFloat width = CGRectGetWidth(pieView.bounds); 16 | pieView.path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(pieView.bounds, width/4, width/4)]; 17 | pieView.lineWidth = width/2; 18 | pieView.fillColor = nil; 19 | pieView.strokeColor = [UIColor blackColor]; 20 | [self.view addSubview:pieView]; 21 | 22 | UIViewAnimationOptions options = UIViewAnimationOptionAutoreverse | UIViewAnimationOptionRepeat; 23 | [UIView animateWithDuration:1.0f delay:0.0f options:options animations:^{ 24 | pieView.strokeEnd = 0.0f; 25 | } completion:nil]; 26 | ``` 27 | 28 | ![Example](https://raw.github.com/conradev/CKShapeView/screenshots/Example.gif) 29 | 30 | ## License 31 | 32 | CKShapeView is available under the MIT license. See the LICENSE file for more info. 33 | --------------------------------------------------------------------------------