coretext shows text from bottom to top of textfield - ios

I'm drawing a textfield and i've added a textfield into the UIView. The text is within a path. It draws correctly, but the content starts at the bottom and ends at the top. Is there a way to fix this?
Here's my code:
// Draw text into a circle using Core Text and Quartz
- (void) drawRect:(CGRect)rect
{
[super drawRect: rect];
CGContextRef context = UIGraphicsGetCurrentContext();
self.layer.rasterizationScale = [[UIScreen mainScreen] scale];
// Flip the context
CGContextTranslateCTM(context, 0, 3);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, -1.0f));
// Stroke that path
CGContextAddPath(context, backPath);
CGContextSetLineWidth(context, 1.0f);
//[[UIColor blackColor] setStroke];
CGContextStrokePath(context);
// Fill that path
CGContextAddPath(context, backPath);
[[UIColor whiteColor] setFill];
CGContextFillPath(context);
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((CFAttributedStringRef)string);
CTFrameRef theFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, string.length),backPath, NULL);
CTFrameDraw(theFrame, context);
CFRelease(framesetter);
CFRelease(theFrame);
CFRelease(backPath);
}
This how I make the path:
- (id) initWithAttributedString: (NSAttributedString *) aString path: (vector<ofPoint>)paths
{
backPath = CGPathCreateMutable();
CGPathMoveToPoint(backPath, NULL, paths[0].x, paths[0].y); // Bottom left
for(int x=1; x< paths.size(); x++){
CGPathAddLineToPoint(backPath, NULL, paths[x].x,paths[x].y); // Bottom right
}
CGPathCloseSubpath(backPath);
if (!(self = [super initWithFrame:CGRectZero])) return self;
self.backgroundColor = [UIColor clearColor];
string = aString;
return self;
}
And to make the text field:
path.push_back(ofPoint(ofGetWidth()/4,ofGetHeight()/4));
// point to user
// path.push_back(ofPoint((position.x-(ofGetWidth()/4))/2,ofGetHeight()/4));
// point to right top
path.push_back(ofPoint(ofGetWidth()/4,ofGetHeight()/4));
// point to end x, pos y user
path.push_back(ofPoint((ofGetWidth()/4),(position.y-(ofGetHeight()/4))/2));
path.push_back(ofPoint((position.x-(ofGetWidth()/4))/2-100,(position.y-(ofGetHeight()/4))/2-100));
path.push_back(ofPoint((position.x-(ofGetWidth()/4))/2,(position.y-(ofGetHeight()/4))/2));

You are flipping the text matrix. Instead, flip the whole CTM:
CGContextScaleCTM(context, 1, -1);
CGContextTranslateCTM(context, 0, self.bounds.size.height);

Related

CGContextDrawPath only strokes and does not fill

I am trying to make a shape that strokes and fills but it does not fill.
Here is my code:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
CGMutablePathRef pathRef = CGPathCreateMutable();
CGContextBeginPath(ctx);
self.dottedLineColor = [UIColor whiteColor];
self.solidLineColor = [UIColor blackColor];
CGContextSetStrokeColorWithColor(ctx, [self.dottedLineColor CGColor]);
CGContextSetFillColorWithColor(ctx, [UIColor blackColor].CGColor);
CGContextSetLineWidth(ctx, 11.0);
for (NSArray *array in previewPoints){
CGPoint pointMadeFromString1 = CGPointFromString([array objectAtIndex:0]);
CGPoint pointMadeFromString2 = CGPointFromString([array objectAtIndex:1]);
CGPathMoveToPoint(pathRef, NULL, pointMadeFromString1.x, pointMadeFromString1.y);
CGPathAddLineToPoint(pathRef, NULL, pointMadeFromString2.x, pointMadeFromString2.y);
/*
CGContextMoveToPoint(ctx, pointMadeFromString1.x, pointMadeFromString1.y);
CGContextAddLineToPoint(ctx, pointMadeFromString2.x, pointMadeFromString2.y);
CGContextSetLineWidth(ctx, 11.0);
const CGFloat dashPattern[2] = {2, 0};
CGContextSetLineDash(ctx, 2, dashPattern, 2);
CGContextStrokePath(ctx);
*/
}
CGContextAddPath(ctx, pathRef);
CGContextClosePath(ctx);
CGContextDrawPath(ctx, kCGPathFillStroke);
CGContextFillPath(ctx);
}
Here is it how it looks now:
The area in the middle should be black.
How would I fix this?
The issue was that every time I was using CGPathMoveToPoint which would mean that it couldn't fill since it was not actually a closed path. I switched it so that only the first item in the array would use CGPathMoveToPoint and then all of the others would use CGPathAddLineToPoint.

Core Text - Position of text is incorrect

This is the first time I've used core graphics and text so could be doing something very simple thats incorrect. I've been reading the documentation to try and see where i'm going wrong but can't see it so wonder if someone can help me spot the issue.
I'm trying to draw a simple little banner of information at the top of the screen but the text i want to appear in the banner is not drawn in the correct place.
In my view i'm creating my control as follows:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.banner = [[Banner alloc] initWithFrame:CGRectMake(0,20,768,200)];
[self.view addSubview:self.banner];
}
The controls code is as follows:
#import <CoreText/CoreText.h>
#import "Banner.h"
#implementation Banner
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Get the graphics context.
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw the banner border.
CGContextSaveGState(context);
UIColor *strokeColor = [UIColor blackColor];
CGContextSetStrokeColorWithColor(context, strokeColor.CGColor);
CGContextSetLineWidth(context, 5);
CGContextStrokeRect(context, CGRectMake(2,2,self.frame.size.width -4,100));
CGContextRestoreGState(context);
// Setup the text label & value.
NSString *addressLabelText = #"Address";
NSString *addressValue = #"123 Letsby Avenue\nSome Place\nSome City\nSome County\nSome Postcode";
UIFont *font = [UIFont fontWithName:#"Helvetica" size:14.0];
CGSize labelSize = [self getBoundsForString:addressLabelText usingFont:font];
CGSize valueSize = [self getBoundsForString:addressValue usingFont:font];
// So I want to Draw the label at X:10 Y:10 and the Value At X:65 Y:10
// So we get Something like:
// =============================
// || Address 123 letsby avenue
// || Some Place
// || Some City
// || Some County
// || Some Postcode
// =============================
CGRect labelBounds = CGRectMake(10,10,labelSize.width,labelSize.height);
// Move the address value over the margin width(10) + the label width + some spacing(5)
CGRect valueBounds = CGRectMake(10 + labelSize.width + 5, 10, valueSize.width, valueSize.height);
[self drawString: addressLabelText withFont: font inRect:labelBounds usingContext:context];
[self drawString: addressValue withFont: font inRect:valueBounds usingContext:context];
// The text hasn't drawn where i thought it should. Draw a rect there to make sure i had the right bounds
UIColor *rectFillColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.3];
CGContextSaveGState(context);
CGContextSetFillColorWithColor(context, [rectFillColor CGColor]);
CGContextFillRect(context, labelBounds);
CGContextFillRect(context, valueBounds);
CGContextRestoreGState(context);
//Hmm that is drawing in the right place. Whats going wrong with drawing the text.
[self setNeedsDisplay];
}
-(void) drawString:(NSString*) string withFont:(UIFont*)font inRect:(CGRect)rect usingContext:(CGContextRef) context
{
CGContextSaveGState(context);
// flipping the coordinate system.
CGContextTranslateCTM(context, 0, 200); // The control is rendered in a frame 200 high.
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
// Define the path we are going to draw the text on.
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, rect);
CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0);
CFAttributedStringReplaceString (attrString, CFRangeMake(0, 0), (CFStringRef)string);
// Create the framesetter with the attributed string.
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString);
CFRelease(attrString);
// Create a frame.
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL);
// Draw the specified frame in the given context.
CTFrameDraw(frame, context);
CGContextRestoreGState(context);
CFRelease(frame);
CFRelease(path);
CFRelease(framesetter);
}
-(CGSize) getBoundsForString:(NSString *)string usingFont:(UIFont *)font
{
return [string boundingRectWithSize:CGSizeMake(999,999)
options:NSStringDrawingUsesLineFragmentOrigin
attributes:[NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]
context:nil].size;
}
#end
The result of this is some blue rects drawn in the banner where i would expect the text to be but the text actually appears outside of the banner and I'm puzzled why.
https://www.dropbox.com/s/q2ap9tl4okaka49/Banner.png
I've found what seems to work for my code above... changing this:
-(void) drawString:(NSString*) string withFont:(UIFont*)font inRect:(CGRect)rect usingContext:(CGContextRef) context
{
CGContextSaveGState(context);
// flipping the coordinate system.
CGContextTranslateCTM(context, 0, 200); // The control is rendered in a frame 200 high.
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
to
-(void) drawString:(NSString*) string withFont:(UIFont*)font inRect:(CGRect)rect usingContext:(CGContextRef) context
{
CGContextSaveGState(context);
// flipping the coordinate system.
CGContextTranslateCTM(context, 0, rect.size.height+(2*rect.origin.y));
CGContextScaleCTM(context, 1.0, -1.0);
The line following line doesn't seem to change anything.
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
And changing this line:
CGContextTranslateCTM(context, 0, 200);
to
CGContextTranslateCTM(context, 0, rect.size.height+(2*rect.origin.y));
places the text correctly where i'd expect it to be.... using Xamarin and the equivalent c# I have to invert the result of the height + 2Y result :-S

Rotating a NSString in drawRect

I want to rotate my NSString that's at a set location so that it reads from bottom to up.
I know the issue lies in the CGContextTranslateCTM, but I'm not sure how to fix it. If I comment this line, I see my text where I want it, just not rotated. This must be moving my NSString to where it is not visible.
Thoughts?
Here's what I have tried:
NSString * xString = [NSString stringWithFormat:#"%#", self.xStringValue];
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor blackColor] CGColor]);
CGContextSaveGState(UIGraphicsGetCurrentContext());
CGContextTranslateCTM(UIGraphicsGetCurrentContext(), 0, 0);
CGContextRotateCTM(UIGraphicsGetCurrentContext(), M_PI/2);
[xString drawInRect:(CGRectMake(x, y, width, height) withFont:self.Font];
CGContextRestoreGState(UIGraphicsGetCurrentContext());
EDIT:
The code above is drawing my NSString values, but they are being positioned off screen where I can not see them.
This code is actually getting them on screen, just not in the right place.
X and Y are both calculated distances.
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGAffineTransform transform = CGAffineTransformMakeRotation(-90.0 * M_PI/180.0);
CGContextConcatCTM(context, transform);
CGContextTranslateCTM(context, -rect.size.height-x, rect.size.height-y);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor blackColor] CGColor]);
[xString drawInRect:CGRectMake(x, y, w, h) withFont:self.textFont];
CGContextRestoreGState(context);
}
EDIT THREE:
Solution:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextTranslateCTM(context, point.x, point.y);
CGAffineTransform textTransform = CGAffineTransformMakeRotation(-1.57);
CGContextConcatCTM(context, textTransform);
CGContextTranslateCTM(context, -point.x, -point.y);
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor whiteColor] CGColor]);
[string drawInRect:CGRectMake(x, y, width, height) withFont:font];
CGContextRestoreGState(context);
If you want to rotate a UILabel vertically (90°) you can do :
[_myLabel setTransform:CGAffineTransformMakeRotation(-M_PI / 2)];
The result for a UILabel :

Core Graphics - Shadow not seen

I wrote a simple drawRect with a intention of drawing a circle, drop a shadow and fill it with blue. I have successfully achieved drawing a circle and filling it in blue but my shadow is not in effect. Instead of fill if I stroke my circle, i see the shadow is inside the circle and during fill it is drawn over by fill colour
rect to my drawRect has dimension [[CustomButton alloc]initWithFrame:CGRectMake(0, 0, 50, 50)];
#implementation CustomButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setBackgroundColor:[UIColor clearColor]];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef colorRef = [[UIColor blueColor] CGColor];
CGContextSetStrokeColorWithColor(context, colorRef);
CGContextSetFillColorWithColor(context, [[UIColor blueColor] CGColor]);
CGRect buttonRect = CGRectInset(rect, 3, 3);
CGPoint centerPoint = CGPointMake(CGRectGetMidX(buttonRect ), CGRectGetMidY(buttonRect));
CGFloat radius = CGRectGetWidth(buttonRect) / 2;
CGContextSaveGState(context);
CGContextSetShadow(context, CGSizeMake(15.0, 20.0), 1.0);
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, 0, 2*M_PI, 1);
CGContextClip(context);
CGContextFillRect(context, buttonRect);
CGContextRestoreGState(context);
CGColorRelease(colorRef);
}
#end
Thank you
Try this:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGColorRef colorRef = [[UIColor blueColor] CGColor];
CGContextSetStrokeColorWithColor(context, colorRef);
CGContextSetFillColorWithColor(context, [[UIColor blueColor] CGColor]);
CGRect buttonRect = CGRectInset(rect, 3, 3);
CGPoint centerPoint = CGPointMake(CGRectGetMidX(buttonRect ), CGRectGetMidY(buttonRect));
CGFloat radius = CGRectGetWidth(buttonRect) / 2;
CGContextSaveGState(context);
CGContextSetShadow(context, CGSizeMake(2.0, 2.0), 2.0);
CGContextAddArc(context, centerPoint.x, centerPoint.y, radius, 0, 2*M_PI, 1);
//CGContextClip(context);
CGContextFillPath(context);
CGContextRestoreGState(context);
CGColorRelease(colorRef);
}
Your shadow was rectangular and that is why it was not seen under the circle. I changed the call CGContextFillRect to CGContextFillPath, the path which you already create using CGContextAddArc.
Is this what you were going for?
EDIT
You can find a project here: https://bitbucket.org/reydan/so_circleshadow

iOS Brush Hardness like Photoshop

How to get the following brush smoothness(hardness) effect like photoshop?
My attempt:
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, 30);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:0.5f].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0, 0), 20.0f, [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:1.0f].CGColor);
CGContextAddPath(context, path);
CGContextStrokePath(context);
CGContextRestoreGState(context);
I tried adjusting alpha values, and shadow blur factor, but no successful result.
Does anybody have a solution to this? Any help would be appreciated.
On this image you can see following code result. I believe it is almost same to what you want.
Just outer shadow is not just enough to give that smooth effect that is why I add some inner shadow to shape with white color.
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Shadows
UIColor* shadow = UIColor.redColor;
CGSize shadowOffset = CGSizeMake(0.1, -0.1);
CGFloat shadowBlurRadius = 11;
UIColor* shadow2 = UIColor.whiteColor; // Here you can adjust softness of inner shadow.
CGSize shadow2Offset = CGSizeMake(0.1, -0.1);
CGFloat shadow2BlurRadius = 9;
// Rectangle Drawing
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(59, 58, 439, 52) cornerRadius: 21];
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, [shadow CGColor]);
[UIColor.redColor setFill];
[rectanglePath fill];
// Rectangle Inner Shadow
CGContextSaveGState(context);
UIRectClip(rectanglePath.bounds);
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
CGContextSetAlpha(context, CGColorGetAlpha([shadow2 CGColor]));
CGContextBeginTransparencyLayer(context, NULL);
{
UIColor* opaqueShadow = [shadow2 colorWithAlphaComponent: 1];
CGContextSetShadowWithColor(context, shadow2Offset, shadow2BlurRadius, [opaqueShadow CGColor]);
CGContextSetBlendMode(context, kCGBlendModeSourceOut);
CGContextBeginTransparencyLayer(context, NULL);
[opaqueShadow setFill];
[rectanglePath fill];
CGContextEndTransparencyLayer(context);
}
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
CGContextRestoreGState(context);
}
Regarding size of the shape you have to adjust both inner and outer shadows blur radius.
You can get an effect similar to what you're trying to achieve by blending your shadow with your stroke
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, path);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetShadowWithColor(context, CGSizeMake(0.f, 0.f), self.lineWidth/4, [self.lineColor CGColor]);
CGContextSetBlendMode(context, kCGBlendModeMultiply);
CGContextSetAlpha(context, self.lineAlpha);
CGContextStrokePath(context);
With Multiply blending mode, using white color as stroke color and setting the color of the brush you want to the shadow, you get the following result:
I've connected the drawing function to touchesMoved event, so that way the longer I take to paint a part of the image, the harder the "Brush" draws (see the black line).
This probably isn't the perfect answer, but it's the best I can do for my needs.
Grab the FXBlurView: https://github.com/nicklockwood/FXBlurView
You can either draw your strokes on an FXBlurView or convert your UIView to UIImage after you've finished drawing (using the code I took from this answer https://stackoverflow.com/a/22494886/505259):
+ (UIImage *) imageWithView:(UIView *)view
{
UIGraphicsBeginImageContextWithOptions(view.bounds.size, view.opaque, 0.0f);
[view drawViewHierarchyInRect:view.bounds afterScreenUpdates:NO];
UIImage * snapshotImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return snapshotImage;
}
and use FXBlurView's category on UIImage:
- (UIImage *)blurredImageWithRadius:(CGFloat)radius
iterations:(NSUInteger)iterations
tintColor:(UIColor *)tintColor;
to blur the resulting image, giving it a Photoshop soft brush like appearance.
I'm still looking for a real answer though. I have an OpenCV project that requires an exact replica of Photoshop's soft brush tool.
I've been working on drawing the path with inner glow, and somehow succeeded (at least for my taste).
I've implemented the drawing code on top of the levinunnick's Smooth-Line-View. The code is MIT licensed, so you'll need to add it to your project.
Currently you can assign the line color, width and the smoothness for the line you want to draw. Be careful with smoothness, use a float between 0 - 1. I've changed the touch methods cause I needed to access the drawing methods from another view. Check the original code, if you want to revert to the touch methods.
I did not optimize the code, if you've got a better idea, just edit this answer.
Here is the H file:
#interface LineView : UIView
- (instancetype)initWithFrame:(CGRect)frame andColor:(UIColor *)lineColor andWidth:(CGFloat)lineWidth andSmoothness:(CGFloat)lineSmooth;
- (void)touchStartedWith:(CGPoint)location;
- (void)touchMovedWith:(CGPoint)location;
#end
This is the M file:
#import "LineView.h"
static const CGFloat kPointMinDistance = 0.05f;
static const CGFloat kPointMinDistanceSquared = kPointMinDistance * kPointMinDistance;
#interface LineView ()
#property (strong) UIColor *lineColor;
#property (assign) CGFloat lineWidth;
#property (assign) CGFloat lineSmooth;
#property (assign) CGPoint currentPoint;
#property (assign) CGPoint previousPoint;
#property (assign) CGPoint previousPreviousPoint;
#end
#implementation LineView
{
#private
CGMutablePathRef _path;
}
- (instancetype)initWithFrame:(CGRect)frame andColor:(UIColor *)lineColor andWidth:(CGFloat)lineWidth andSmoothness:(CGFloat)lineSmooth
{
self = [super initWithFrame:frame];
if ( self ) {
_path = CGPathCreateMutable();
if ( lineSmooth < 0 ) lineSmooth = 0;
if ( lineSmooth > 1 ) lineSmooth = 1;
self.backgroundColor = [UIColor clearColor];
self.lineColor = lineColor;
self.lineWidth = lineWidth;
self.lineSmooth = lineWidth * ( lineSmooth / 4 );
self.opaque = NO;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
[self.backgroundColor set];
UIRectFill(rect);
#autoreleasepool {
CGColorRef theColor = self.lineColor.CGColor;
UIColor *theClearOpaque = [[UIColor whiteColor] colorWithAlphaComponent:1];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextAddPath(context, _path);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, theColor);
// Outer shadow
CGSize shadowOffset = CGSizeMake(0.1f, -0.1f);
CGFloat shadowBlurRadius = self.lineSmooth;
CGContextSetShadowWithColor(context, shadowOffset, shadowBlurRadius, theColor);
CGContextStrokePath(context);
if ( self.lineSmooth > 0 ) {
// Inner shadow
CGRect bounds = CGPathGetBoundingBox(_path);
CGRect drawBox = CGRectInset(bounds, -2.0f * self.lineWidth, -2.0f * self.lineWidth);
CGContextSaveGState(context);
UIRectClip(drawBox);
CGContextSetShadowWithColor(context, CGSizeZero, 0, NULL);
CGContextSetAlpha(context, CGColorGetAlpha(theClearOpaque.CGColor));
CGContextBeginTransparencyLayer(context, NULL);
{
// Outer shadow
UIColor *oShadow = [theClearOpaque colorWithAlphaComponent:1];
CGContextSetShadowWithColor(context, CGSizeMake(0.1f, -0.1f), self.lineWidth / 64 * self.lineSmooth, oShadow.CGColor);
CGContextSetBlendMode(context, kCGBlendModeSourceOut);
CGContextBeginTransparencyLayer(context, NULL);
[oShadow setFill];
// Draw the line again
CGContextAddPath(context, _path);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineWidth(context, self.lineWidth);
CGContextSetStrokeColorWithColor(context, oShadow.CGColor);
CGContextStrokePath(context);
CGContextEndTransparencyLayer(context);
}
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
}
}
}
- (void)touchStartedWith:(CGPoint)location
{
self.previousPoint = location;
self.previousPreviousPoint = location;
self.currentPoint = location;
[self touchMovedWith:location];
}
- (void)touchMovedWith:(CGPoint)location
{
CGRect drawBox;
#autoreleasepool {
CGFloat dx = location.x - self.currentPoint.x;
CGFloat dy = location.y - self.currentPoint.y;
if ( ( dx * dx + dy * dy ) < kPointMinDistanceSquared ) {
return;
}
self.previousPreviousPoint = self.previousPoint;
self.previousPoint = self.currentPoint;
self.currentPoint = location;
CGPoint mid1 = midPoint(self.previousPoint, self.previousPreviousPoint);
CGPoint mid2 = midPoint(self.currentPoint, self.previousPoint);
CGMutablePathRef subpath = CGPathCreateMutable();
CGPathMoveToPoint(subpath, NULL, mid1.x, mid1.y);
CGPathAddQuadCurveToPoint(subpath, NULL, self.previousPoint.x, self.previousPoint.y, mid2.x, mid2.y);
CGRect bounds = CGPathGetBoundingBox(subpath);
drawBox = CGRectInset(bounds, -2.0f * self.lineWidth, -2.0f * self.lineWidth);
CGPathAddPath(_path, NULL, subpath);
CGPathRelease(subpath);
}
[self setNeedsDisplayInRect:drawBox];
}
- (void)dealloc
{
CGPathRelease(_path);
_path = NULL;
}
#end

Resources