-
-
Save vinod1988/5037834 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import <QuartzCore/QuartzCore.h> | |
@interface ArrowLayer : CALayer | |
@property (nonatomic) CGFloat thickness; | |
@property (nonatomic) CGFloat startRadians; | |
@property (nonatomic) CGFloat lengthRadians; | |
@property (nonatomic) CGFloat headLengthRadians; | |
@property (nonatomic, strong) UIColor *fillColor; | |
@property (nonatomic, strong) UIColor *strokeColor; | |
@property (nonatomic) CGFloat lineWidth; | |
@property (nonatomic) CGLineJoin lineJoin; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#import "ArrowLayer.h" | |
#import <objc/runtime.h> | |
@implementation ArrowLayer | |
@dynamic thickness; | |
@dynamic startRadians; | |
@dynamic lengthRadians; | |
@dynamic headLengthRadians; | |
@dynamic fillColor; | |
@dynamic strokeColor; | |
@dynamic lineWidth; | |
@dynamic lineJoin; | |
+ (NSSet *)customPropertyKeys { | |
static NSMutableSet *set; | |
static dispatch_once_t once; | |
dispatch_once(&once, ^{ | |
unsigned int count; | |
objc_property_t *properties = class_copyPropertyList(self, &count); | |
set = [[NSMutableSet alloc] initWithCapacity:count]; | |
for (int i = 0; i < count; ++i) { | |
[set addObject:@(property_getName(properties[i]))]; | |
} | |
free(properties); | |
}); | |
return set; | |
} | |
+ (BOOL)needsDisplayForKey:(NSString *)key { | |
return [[self customPropertyKeys] containsObject:key] || [super needsDisplayForKey:key]; | |
} | |
- (id)initWithLayer:(id)layer { | |
if (self = [super initWithLayer:layer]) { | |
for (NSString *key in [self.class customPropertyKeys]) { | |
[self setValue:[layer valueForKey:key] forKey:key]; | |
} | |
} | |
return self; | |
} | |
- (BOOL)needsDisplayOnBoundsChange { | |
return YES; | |
} | |
- (void)drawInContext:(CGContextRef)gc { | |
[self moveOriginToCenterInContext:gc]; | |
[self addArrowToPathInContext:gc]; | |
[self drawPathOfContext:gc]; | |
} | |
- (void)moveOriginToCenterInContext:(CGContextRef)gc { | |
CGRect bounds = self.bounds; | |
CGContextTranslateCTM(gc, CGRectGetMidX(bounds), CGRectGetMidY(bounds)); | |
} | |
- (void)addArrowToPathInContext:(CGContextRef)gc { | |
CGFloat startRadians; | |
CGFloat headRadians; | |
CGFloat tipRadians; | |
[self getStartRadians:&startRadians headRadians:&headRadians tipRadians:&tipRadians]; | |
CGFloat thickness = self.thickness; | |
CGFloat outerRadius = self.bounds.size.width / 2; | |
CGFloat tipRadius = outerRadius - thickness / 2; | |
CGFloat innerRadius = outerRadius - thickness; | |
BOOL outerArcIsClockwise = tipRadians > startRadians; | |
CGContextMoveToPoint(gc, tipRadius * cosf(tipRadians), tipRadius * sinf(tipRadians)); | |
CGContextAddArc(gc, 0, 0, outerRadius, headRadians, startRadians, outerArcIsClockwise); | |
CGContextAddArc(gc, 0, 0, innerRadius, startRadians, headRadians, !outerArcIsClockwise); | |
CGContextClosePath(gc); | |
} | |
- (void)getStartRadians:(CGFloat *)startRadiansOut headRadians:(CGFloat *)headRadiansOut tipRadians:(CGFloat *)tipRadiansOut { | |
*startRadiansOut = self.startRadians; | |
CGFloat lengthRadians = self.lengthRadians; | |
CGFloat headLengthRadians = [self clippedHeadLengthRadians]; | |
// Compute headRadians carefully so it is exactly equal to startRadians if the head length was clipped. | |
*headRadiansOut = *startRadiansOut + (lengthRadians - headLengthRadians); | |
*tipRadiansOut = *startRadiansOut + lengthRadians; | |
} | |
- (CGFloat)clippedHeadLengthRadians { | |
CGFloat lengthRadians = self.lengthRadians; | |
CGFloat headLengthRadians = copysignf(self.headLengthRadians, lengthRadians); | |
if (fabsf(headLengthRadians) > fabsf(lengthRadians)) { | |
headLengthRadians = lengthRadians; | |
} | |
return headLengthRadians; | |
} | |
- (void)drawPathOfContext:(CGContextRef)gc { | |
CGPathDrawingMode mode = 0; | |
[self setFillPropertiesOfContext:gc andUpdateMode:&mode]; | |
[self setStrokePropertiesOfContext:gc andUpdateMode:&mode]; | |
CGContextDrawPath(gc, mode); | |
} | |
- (void)setFillPropertiesOfContext:(CGContextRef)gc andUpdateMode:(CGPathDrawingMode *)modeInOut { | |
UIColor *fillColor = self.fillColor; | |
if (fillColor) { | |
*modeInOut |= kCGPathFill; | |
CGContextSetFillColorWithColor(gc, fillColor.CGColor); | |
} | |
} | |
- (void)setStrokePropertiesOfContext:(CGContextRef)gc andUpdateMode:(CGPathDrawingMode *)modeInOut { | |
UIColor *strokeColor = self.strokeColor; | |
CGFloat lineWidth = self.lineWidth; | |
if (strokeColor && lineWidth > 0) { | |
*modeInOut |= kCGPathStroke; | |
CGContextSetStrokeColorWithColor(gc, strokeColor.CGColor); | |
CGContextSetLineWidth(gc, lineWidth); | |
CGContextSetLineJoin(gc, self.lineJoin); | |
} | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment