I've got an question about UIBezierPath.
For example I've got this path:
Now I want to have a color gradient from white to red. From left to right.
Here is my code:
UIBezierPath *bezierPath;
bezierPath = [UIBezierPath bezierPathWithArcCenter:_center radius:_radius startAngle:((4 * angle)) endAngle:(((20) * angle)) clockwise:YES];
[bezierPath addLineToPoint:_center];
[bezierPath closePath];
UIColor *color = [UIColor colorWithHue:0/sectors saturation:1. brightness:1. alpha:1];
[color setFill];
[color setStroke];
[bezierPath fill];
[bezierPath stroke];
Can anyone help me?
Edit 1:
I have this color wheel:
UIBezierPath *bezierPath;
for ( int i = 0; i < 360; i++) {
bezierPath = [UIBezierPath bezierPathWithArcCenter:_center radius:_radius startAngle:((i * angle)) endAngle:(((i + 1) * angle)) clockwise:YES];
[bezierPath addLineToPoint:_center];
[bezierPath closePath];
UIColor *color = [UIColor colorWithHue:i/sectors saturation:1. brightness:1. alpha:1];
[color setFill];
[color setStroke];
[bezierPath fill];
[bezierPath stroke];
}
but I want this: (With the white Gradient)
Use CAGradientLayer and mask it using CAShapeLayer Something like this
- (void)addCircle{
CAShapeLayer *shapeLayer = [CAShapeLayer new];
shapeLayer.path = [UIBezierPath bezierPathWithOvalInRect:CGRectInset(CGRectMake(10, 10, 100, 100), 4, 4)].CGPath;
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.contentsScale = [UIScreen mainScreen].scale;
shapeLayer.shouldRasterize = NO;
CAGradientLayer *_gradientLayer = [CAGradientLayer layer];
_gradientLayer.frame =self.view.bounds;
_gradientLayer.startPoint = CGPointMake(0.0, 1);
_gradientLayer.endPoint = CGPointMake(1, 0);
_gradientLayer.colors = #[(id)[UIColor blueColor].CGColor,(id)[UIColor redColor].CGColor];
//Add gradient layer to view
[self.view.layer addSublayer:_gradientLayer];
_gradientLayer.mask = shapeLayer;
}
Above method will add a triangle may be you need to change start and end points. Also you can change gradient values to whatever you need.
Apple Docs
CAGradientLayer Tutorial
Update After update its more clear that its not triangle you want, but what you need is possible with CAGradientLayer and CAShapeLayer, you need to follow same approach where you can add gradient with different colors and locations (stops)(if you are adding locations then make sure locations and colors are equal) and then mask it with CAShapeLayer which was circle.
you can give it a try :)
- (void)drawRect:(CGRect)rect
{
CGFloat arcStep = (M_PI *2) / 360; // M_PI*2 is equivalent of full cirle
BOOL clocklwise = NO;
CGFloat x = CGRectGetWidth(rect) / 2; // circle's center
CGFloat y = CGRectGetHeight(rect) / 2; // circle's center
CGFloat radius = MIN(x, y) / 2;
CGContextRef ctx = UIGraphicsGetCurrentContext();
// draw colorful circle
CGContextSetLineWidth(ctx, radius*2);
for (CGFloat i = 0; i < 360; i+=1)
{
UIColor* c = [UIColor colorWithHue:i/360 saturation:1. brightness:1. alpha:1];
CGContextSetStrokeColorWithColor(ctx, c.CGColor);
CGFloat startAngle = i * arcStep;
CGFloat endAngle = startAngle + arcStep + 0.02;
CGContextAddArc(ctx, x, y, radius, startAngle, endAngle, clocklwise);
CGContextStrokePath(ctx);
}
// drawing circles then, you might want few of them - smaller radius and less alpha with each step
UIColor* c = [[UIColor whiteColor] colorWithAlphaComponent: 0.03];
for (CGFloat fillRadius = radius/2; fillRadius > 0; fillRadius -= 1.f)
{
CGContextSetLineWidth(ctx, fillRadius*2);
CGContextSetStrokeColorWithColor(ctx, c.CGColor);
CGContextAddArc(ctx, x, y, fillRadius, 0, M_PI * 2, clocklwise);
CGContextStrokePath(ctx);
}
}
Another take on Oleg's answer regarding the saturation (white gradient) that I find more pleasing, and in Swift (3).
You can add steps to the gradient if you want a bigger white circle (ex a 2nd white step at 0.2)
import UIKit
class HSView: UIView {
override func draw(_ rect: CGRect) {
let arcStep = 2 * CGFloat.pi / 360
let isClockwise = false
let x = rect.width / 2
let y = rect.height / 2
let radius = min(x, y) / 2
let ctx = UIGraphicsGetCurrentContext()
ctx?.setLineWidth(2 * radius)
for i in 0..<360 {
let color = UIColor(hue: CGFloat(i)/360, saturation: 1, brightness: 1, alpha: 1)
let startAngle = CGFloat(i) * arcStep
let endAngle = startAngle + arcStep + 0.02
ctx?.setStrokeColor(color.cgColor)
ctx?.addArc(center: CGPoint(x: x, y: y), radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: isClockwise)
ctx?.strokePath()
}
let gradient = CGGradient(colorsSpace: UIColor.white.cgColor.colorSpace,
colors: [
UIColor.white.cgColor,
UIColor.white.withAlphaComponent(0).cgColor,
] as CFArray,
locations: [
0,
1,
]
)
ctx?.drawRadialGradient(gradient!, startCenter: CGPoint(x: x, y: y), startRadius: 0, endCenter: CGPoint(x: x, y: y), endRadius: 2 * radius, options: .drawsAfterEndLocation)
}
}
You can try this:
//// General Declarations
CGContextRef context = UIGraphicsGetCurrentContext();
//// Shadow Declarations
NSShadow* shadow = [[NSShadow alloc] init];
[shadow setShadowColor: UIColor.whiteColor];
[shadow setShadowOffset: CGSizeMake(2.1, -4.1)];
[shadow setShadowBlurRadius: 5];
//// Bezier Drawing
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
//// Bezier 2 Drawing
UIBezierPath* bezier2Path = UIBezierPath.bezierPath;
[bezier2Path moveToPoint: CGPointMake(170.5, 59.5)];
[bezier2Path addCurveToPoint: CGPointMake(170.5, 71.5) controlPoint1: CGPointMake(173.5, 65.5) controlPoint2: CGPointMake(170.5, 71.5)];
[bezier2Path addLineToPoint: CGPointMake(155.5, 57.5)];
[bezier2Path addLineToPoint: CGPointMake(170.5, 59.5)];
[UIColor.redColor setFill];
[bezier2Path fill];
////// Bezier 2 Inner Shadow
CGContextSaveGState(context);
UIRectClip(bezier2Path.bounds);
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
CGContextSetAlpha(context, CGColorGetAlpha([shadow.shadowColor CGColor]));
CGContextBeginTransparencyLayer(context, NULL);
{
UIColor* opaqueShadow = [shadow.shadowColor colorWithAlphaComponent: 1];
CGContextSetShadowWithColor(context, shadow.shadowOffset, shadow.shadowBlurRadius, [opaqueShadow CGColor]);
CGContextSetBlendMode(context, kCGBlendModeSourceOut);
CGContextBeginTransparencyLayer(context, NULL);
[opaqueShadow setFill];
[bezier2Path fill];
CGContextEndTransparencyLayer(context);
}
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
and the result will be:
You will archive it by using QuartsCore. If you want get image with your figure, you can call UIGraphicsBeginImageContext(), if you want draw it on UIView you should overload method - (void)drawRect:(CGRect)rect of UIView.
You can add path to context, and use it for clipping context (Don't forget save the context state before). After you can draw the gradient. The gradient will be clipped by path.
You will get something like this:
CGPathRef path = [bezierPath CGPath];
CGContextSaveGState(context);
CGContextAddPath(context, path);
CGContextClip(context);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSArray *colors = [NSArray arrayWithObjects:(id)[UIColor redColor].CGColor, (id)[UIColor blueColor].CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors( colorSpace, (CFArrayRef)colors, NULL);
CGColorSpaceRelease(colorSpace);
colorSpace = NULL;
CGContextDrawLinearGradient(context, gradient, CGPointMake(0.0, 0.0), CGPointMake(100.0, 100.0), kNilOptions);
CGContextRestoreGState(context);
Related
I'm trying to draw a somewhat similar image to this, using Core Graphics:
I was able to draw the main arc, but I am not able to understand, how to divide arc into parts/draw graduation on arc? My current code to draw the arc is:
[path addArcWithCenter:point radius:radius startAngle:DEGREES_TO_RADIANS(specific_start_angle) endAngle:DEGREES_TO_RADIANS(specific_end_angle) clockwise:NO];
I tried using strokeWithBlendMode but I am facing problem with position of graduations or ticks.
Teja's solution will work fine for you, but it does require that you calculate your own start and end points for the graduations.
I suggest you create a function that let's you draw the graduations at a given angle of the arc, that will calculate the start and end points of the graduations, given a length.
static inline void drawGraduation(CGPoint center, CGFloat radius, CGFloat angle, CGFloat length, CGFloat width, CGColorRef color) {
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat radius2 = radius+length; // The radius of the end points of the graduations
CGPoint p1 = (CGPoint){cos(angle)*radius+center.x, sin(angle)*radius+center.y}; // the start point of the graduation
CGPoint p2 = (CGPoint){cos(angle)*radius2+center.x, sin(angle)*radius2+center.y}; // the end point of the graduation
CGContextMoveToPoint(c, p1.x, p1.y);
CGContextAddLineToPoint(c, p2.x, p2.y);
CGContextSetStrokeColorWithColor(c, color);
CGContextSetLineWidth(c, width);
CGContextStrokePath(c);
}
You can then call this in a for loop (or however you want to do it) when you draw your arc for the main scale of your VU meter. You can also easily customise the color, width and length of given graduations at given intervals (for example, this code gives a thicker & longer red line every 5 graduations).
- (void)drawRect:(CGRect)rect {
CGRect r = self.bounds;
CGFloat startAngle = -M_PI*0.2; // start angle of the main arc
CGFloat endAngle = -M_PI*0.8; // end angle of the main arc
NSUInteger numberOfGraduations = 16;
CGPoint center = (CGPoint){r.size.width*0.5, r.size.height*0.5}; // center of arc
CGFloat radius = (r.size.width*0.5)-20; // radius of arc
CGFloat maxGraduationWidth = 1.5; // the maximum graduation width
CGFloat maxGraduationWidthAngle = maxGraduationWidth/radius; // the maximum graduation width angle (used to prevent the graduations from being stroked outside of the main arc)
// draw graduations
CGFloat deltaArc = (endAngle-startAngle+maxGraduationWidthAngle)/(numberOfGraduations-1); // the change in angle of the arc
CGFloat startArc = startAngle-(maxGraduationWidthAngle*0.5); // the starting angle of the arc
for (int i = 0; i < numberOfGraduations; i++) {
if (i % 5 == 0) {
drawGraduation(center, radius, startArc+(i*deltaArc), 14, 1.5, [UIColor redColor].CGColor); // red graduation every 5 graduations.
} else {
drawGraduation(center, radius, startArc+(i*deltaArc), 10, 1, [UIColor blackColor].CGColor);
}
}
// draw main arc
UIBezierPath* mainArc = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:NO];
[[UIColor blackColor] setStroke];
mainArc.lineWidth = 2;
[mainArc stroke];
}
Output
Full project: https://github.com/hamishknight/VU-Meter-Arc
You can draw bezier path with dashes ,something like this::
//// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(54.5, 62.5)];
[bezierPath addCurveToPoint: CGPointMake(121.5, 39.5) controlPoint1: CGPointMake(54.5, 62.5) controlPoint2: CGPointMake(87.5, 39.5)];
[bezierPath addCurveToPoint: CGPointMake(190.5, 62.5) controlPoint1: CGPointMake(155.5, 39.5) controlPoint2: CGPointMake(190.5, 62.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 10;
CGFloat bezierPattern[] = {24, 2};
[bezierPath setLineDash: bezierPattern count: 2 phase: 0];
[bezierPath stroke];
Else You can draw multiple bezier path and draw it similar to what you want:
Something like this:
For the small line:
UIBezierPath* bezier2Path = [UIBezierPath bezierPath];
[bezier2Path moveToPoint: CGPointMake(65, 45)];
[bezier2Path addLineToPoint: CGPointMake(75, 63)];
[UIColor.blackColor setStroke];
bezier2Path.lineWidth = 1.5;
[bezier2Path stroke];
I am trying to draw arc within a circle and fill the arc's. Here is the code i am using:
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextAddEllipseInRect(ctx, rect);
CGContextSetFillColor(ctx, CGColorGetComponents([[clrArray objectAtIndex:0] CGColor]));
CGContextFillPath(ctx);
CGFloat radius = 12.5;
CGFloat startAngle1 = DegreesToRadians(0);
CGFloat endAngle1 = DegreesToRadians(135);
CGFloat startAngle2 = DegreesToRadians(135);
CGFloat endAngle2 = DegreesToRadians(270);
//draw arc
CGPoint center = CGPointMake(radius,radius);
//Arc 1
CGContextAddArc(ctx,
center.x,
center.y,
radius,
startAngle1,
endAngle1,
YES);
[(UIColor*)[clrArray objectAtIndex:1] set];
CGContextFillPath(ctx);
//Arc 2
CGContextAddArc(ctx,
center.x,
center.y,
radius,
startAngle2,
endAngle2,
YES);
[(UIColor*)[clrArray objectAtIndex:2] set];
CGContextFillPath(ctx);
It is not drawing right. If i only provide one arc then if its angle is less than 180 then it is also not daring right. What i am doing wrong?
Edit: Here is the image of what is happening
I used start angle 0 and end angle 45 in this image
Got another solution using bezier Path
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat radius = 50.0/2.0;
CGPoint center = CGPointMake(radius,radius);
CGFloat startAngle1 = DegreesToRadians(0);
CGFloat endAngle1 = DegreesToRadians(120);
CGFloat startAngle2 = DegreesToRadians(120);
CGFloat endAngle2 = DegreesToRadians(240);
CGContextSaveGState(ctx);
UIBezierPath* clip1 = [UIBezierPath bezierPathWithArcCenter:center
radius:radius
startAngle:startAngle1
endAngle:endAngle1
clockwise:YES];
[clip1 addLineToPoint:center];
[clip1 closePath];
[clip1 addClip];
UIBezierPath *arc1 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 50, 50)];
[[UIColor blackColor] set];
[arc1 fill];
CGContextRestoreGState(ctx);
CGContextSaveGState(ctx);
UIBezierPath* clip2 = [UIBezierPath bezierPathWithArcCenter:center
radius:radius
startAngle:startAngle2
endAngle:endAngle2
clockwise:YES];
[clip2 addLineToPoint:center];
[clip2 closePath];
[clip2 addClip];
UIBezierPath *arc2 = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 50, 50)];
[[UIColor orangeColor] set];
[arc2 fill];
CGContextRestoreGState(ctx);
I'm trying to draw a UIBezierPathShape in iOS7 then apply a shadow. This works great except that when I stroke the path, the stroke shows up behind the shape. How can I rectify this?
Code:
- (void)drawDiamondWithCount:(NSUInteger)count inRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIGraphicsPushContext(ctx);
UIEdgeInsets insets = UIEdgeInsetsMake(cardEdgeInsetTop, cardEdgeInsetRight, cardEdgeInsetBottom, cardEdgeInsetLeft);
CGRect insetsRect = UIEdgeInsetsInsetRect(rect, insets);
CGFloat shapeHeight = insetsRect.size.height / (double) count;
CGRect shapeRect;
for (NSUInteger i = 0; i < count; ++i) {
// Get the rect for the single shape
int numRemainingShapes = count - i - 1;
CGFloat remainingBottomSpace = numRemainingShapes * shapeHeight;
insets = UIEdgeInsetsMake(i * shapeHeight + shapeEdgeInsets, 0, remainingBottomSpace + shapeEdgeInsets, 0);
shapeRect = UIEdgeInsetsInsetRect(insetsRect, insets);
UIBezierPath *path = [self getDiamondPath:shapeRect];
[[UIColor redColor] setFill];
[[UIColor blackColor] setStroke];
UIGraphicsPushContext(ctx);
CGContextSetShadow(ctx, CGSizeMake(5, 2), 5);
[path fill];
UIGraphicsPopContext();
//[path stroke];
}
UIGraphicsPopContext();
}
This gives me what I want, minus the stroke
Uncommenting [path stroke] gives me this. I want the stroke, but don't want to see it behind the shape.
I suspect that instead of UIGraphicsPushContext and UIGraphicsPopContext, I think you want CGContextSaveGState and CGContextRestoreGState:
// create context and configure
CGContextRef ctx = UIGraphicsGetCurrentContext();
[[UIColor redColor] setFill];
[[UIColor blackColor] setStroke];
// create path
UIBezierPath *path = ...;
path.lineJoinStyle = kCGLineJoinMiter;
path.lineWidth = 2.0;
// fill the center with shadow
CGContextSaveGState(ctx);
CGContextSetShadow(ctx, CGSizeMake(5, 2), 5);
[path fill];
CGContextRestoreGState(ctx);
// stroke border without shadow
CGContextSetLineWidth(ctx, 2.0);
[path stroke];
With UIGraphicsPushContext and UIGraphicsPopContext you get:
With CGContextSaveGState and CGContextRestoreGState you get:
I have path with values and I want to make this gradient.
Here is the code:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [[UIColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:171.0/255.0 alpha:0.6] CGColor]);
CGContextSetFillColorWithColor(context, [[UIColor colorWithRed:245.0/255.0 green:245.0/255.0 blue:171.0/255.0 alpha:0.6] CGColor]);
UIBezierPath *aPath = [UIBezierPath bezierPath];
[aPath moveToPoint:CGPointMake(30.0, 100.0)];
[aPath addLineToPoint:CGPointMake(200.0, 120.0)];
[aPath addLineToPoint:CGPointMake(300, 210)];
[aPath addLineToPoint:CGPointMake(300, 420)];
[aPath addLineToPoint:CGPointMake(30, 420.0)];
[aPath addLineToPoint:CGPointMake(30, 100.0)];
[aPath closePath];
[aPath fill];
Any pointers to figure out issue with this code?
First - I've created simple arrow with Bezier path:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(24.5, 1.5)];
[bezierPath addLineToPoint: CGPointMake(2.5, 14.5)];
[bezierPath addLineToPoint: CGPointMake(24.5, 28.5)];
[bezierPath addLineToPoint: CGPointMake(24.5, 1.5)];
[bezierPath closePath];
[[UIColor blackColor] setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
Then I've drawn simple linear gradient from black to white:
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = UIGraphicsGetCurrentContext();
NSArray* simpleLinearGradientColors = [NSArray arrayWithObjects:
(id)[UIColor blackColor].CGColor,
(id)[UIColor whiteColor].CGColor, nil];
CGFloat simpleLinearGradientLocations[] = {0, 1};
CGGradientRef simpleLinearGradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)simpleLinearGradientColors, simpleLinearGradientLocations);
// Bezier Drawing
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(24.5, 1.5)];
[bezierPath addLineToPoint: CGPointMake(2.5, 14.5)];
[bezierPath addLineToPoint: CGPointMake(24.5, 28.5)];
[bezierPath addLineToPoint: CGPointMake(24.5, 1.5)];
[bezierPath closePath];
CGContextSaveGState(context);
[bezierPath addClip];
CGContextDrawLinearGradient(context, simpleLinearGradient, CGPointMake(2.5, 15), CGPointMake(24.5, 15), 0);
CGContextRestoreGState(context);
[[UIColor blackColor] setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CGGradientRelease(simpleLinearGradient);
CGColorSpaceRelease(colorSpace);
That's what I got:
Basically you can create linear, radial gradient with bunch of settings (locations, colors) and of course you should modify code above.
1.Create .h and .m files for eg CustomGradientView
CustomGradientView.h file should look like this
#interface CustomGradientView : UIView
and CustomGradientView.m file should look like this
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
// color1 and color 2 u can take as u wish here i m taking for explaining
UIColor *color1=[UIColor whiteColor];
CGColorRef startColor =color1.CGColor;
UIColor *color2=[UIColor redColor];
CGColorRef endColor = color2.CGColor;
drawLinearGradient(context, rect, startColor, endColor);
//for rounded corners
CGPathRef p = [[UIBezierPath bezierPathWithRoundedRect:rect
5] CGPath];
CGContextAddRect(context, rect);
CGContextAddPath(context, p);
CGContextEOClip(context);
CGContextClearRect(context, rect);
}
then in the .xib files to which view you want to be gradient extend its class CustomGradientView instead of UIView
the drawLinearGradient method is in another class u have to import it
they are
Common.h
#import <Foundation/Foundation.h>
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor);
CGRect rectFor1PxStroke(CGRect rect);
void draw1PxStroke(CGContextRef context, CGPoint startPoint, CGPoint endPoint, CGColorRef color);
void drawGlossAndGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor);
static inline double radians (double degrees) { return degrees * M_PI/180; }
CGMutablePathRef createArcPathFromBottomOfRect(CGRect rect, CGFloat arcHeight);
CGMutablePathRef createRoundedRectForRect(CGRect rect, CGFloat radius);
and Common.m
#import "Common.h"
CGRect rectFor1PxStroke(CGRect rect) {
return CGRectMake(rect.origin.x + 0.5, rect.origin.y + 0.5, rect.size.width - 1, rect.size.height - 1);
}
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor) {
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGFloat locations[] = { 0.0, 1.0 };
NSArray *colors = [NSArray arrayWithObjects:(__bridge id)startColor, (__bridge id)endColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef) colors, locations);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextAddRect(context, rect);
CGContextClip(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
}
void draw1PxStroke(CGContextRef context, CGPoint startPoint, CGPoint endPoint, CGColorRef color) {
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextSetStrokeColorWithColor(context, color);
CGContextSetLineWidth(context, 1.0);
CGContextMoveToPoint(context, startPoint.x + 0.5, startPoint.y + 0.5);
CGContextAddLineToPoint(context, endPoint.x + 0.5, endPoint.y + 0.5);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
void drawGlossAndGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor) {
drawLinearGradient(context, rect, startColor, endColor);
CGColorRef glossColor1 = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.35].CGColor;
CGColorRef glossColor2 = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1].CGColor;
CGRect topHalf = CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height/2);
drawLinearGradient(context, topHalf, glossColor1, glossColor2);
}
CGMutablePathRef createArcPathFromBottomOfRect(CGRect rect, CGFloat arcHeight) {
CGRect arcRect = CGRectMake(rect.origin.x, rect.origin.y + rect.size.height - arcHeight,
rect.size.width, arcHeight);
CGFloat arcRadius = (arcRect.size.height/2) + (pow(arcRect.size.width, 2) / (8*arcRect.size.height));
CGPoint arcCenter = CGPointMake(arcRect.origin.x + arcRect.size.width/2, arcRect.origin.y + arcRadius);
CGFloat angle = acos(arcRect.size.width / (2*arcRadius));
CGFloat startAngle = radians(180) + angle;
CGFloat endAngle = radians(360) - angle;
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, arcCenter.x, arcCenter.y, arcRadius, startAngle, endAngle, 0);
CGPathAddLineToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect));
CGPathAddLineToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGPathAddLineToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect));
return path;
}
CGMutablePathRef createRoundedRectForRect(CGRect rect, CGFloat radius) {
I want to animate the position of an inner shadow on a bezier I created using PaintCode. The offset of the shadow will be animated, here's two examples:
How can I make this an animatable property so that I can animate the movement of the shadow? The generated code (pasted into drawRect) looks like this:
//// General Declarations
CGContextRef context = UIGraphicsGetCurrentContext();
//// Color Declarations
UIColor* fillColor = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 1];
UIColor* strokeColor = [UIColor colorWithRed: 0 green: 0 blue: 0 alpha: 0.508];
//// Shadow Declarations
UIColor* shadow = strokeColor;
CGSize shadowOffset = CGSizeMake(3.1, 3.1);
CGFloat shadowBlurRadius = 5;
//// Oval Drawing
UIBezierPath* ovalPath = [UIBezierPath bezierPathWithOvalInRect: CGRectMake(31.5, 33.5, 26, 26)];
[fillColor setFill];
[ovalPath fill];
////// Oval Inner Shadow
CGRect ovalBorderRect = CGRectInset([ovalPath bounds], -shadowBlurRadius, -shadowBlurRadius);
ovalBorderRect = CGRectOffset(ovalBorderRect, -shadowOffset.width, -shadowOffset.height);
ovalBorderRect = CGRectInset(CGRectUnion(ovalBorderRect, [ovalPath bounds]), -1, -1);
UIBezierPath* ovalNegativePath = [UIBezierPath bezierPathWithRect: ovalBorderRect];
[ovalNegativePath appendPath: ovalPath];
ovalNegativePath.usesEvenOddFillRule = YES;
CGContextSaveGState(context);
{
CGFloat xOffset = shadowOffset.width + round(ovalBorderRect.size.width);
CGFloat yOffset = shadowOffset.height;
CGContextSetShadowWithColor(context,
CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
shadowBlurRadius,
shadow.CGColor);
[ovalPath addClip];
CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(ovalBorderRect.size.width), 0);
[ovalNegativePath applyTransform: transform];
[[UIColor grayColor] setFill];
[ovalNegativePath fill];
}
CGContextRestoreGState(context);
[strokeColor setStroke];
ovalPath.lineWidth = 1;
[ovalPath stroke];