drawRect method get called but the UIView cannot display - ios

ModeButtonView is a subclass of UIView, and is already added as a subview of viewController.view:
#interface ModeButtonView ()
#property(nonatomic, strong) MonthModeButtonView *monthModeButton;
#end
-(instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self) {
return nil;
}
self.backgroundColor = [UIColor colorWithRed:0.192 green:0.663 blue:0.647 alpha:1];
self.monthModeButton = [[MonthModeButtonView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 0.5 * self.bounds.size.width, 0.5 * self.bounds.size.height)];
self.monthModeButton.center = self.center;
[self addSubview:self.monthModeButton];
return self;
}
The MonthModeButtonView is also a subclass of UIView, and it is added as a subview of ModeButtonView. Its drawRect method is as follows:
- (void)drawRect:(CGRect)rect {
NSLog(#"drawRect get called");
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, rect.origin.x, rect.origin.y);
CGPathAddLineToPoint(path, NULL, rect.size.width, rect.size.height);
CGPathMoveToPoint(path, NULL, rect.size.width, rect.origin.y);
CGPathAddLineToPoint(path, NULL, rect.origin.x, rect.size.height);
CGContextRef currentContext = UIGraphicsGetCurrentContext();
CGContextAddPath(currentContext, path);
[[UIColor redColor] setStroke];
CGContextDrawPath(currentContext, kCGPathStroke);
CGPathRelease(path);
}
I run the program, the ModeButtonView is displayed well on the screen, and the drawRect method of MonthModeButtonView also get called, however, it cannot be displayed on the screen, while I have added it as a subview of ModeButtonView.
However, if I put the code of MonthModeButtonView's drawRect method into ModeButtonView's drawRect method directly, it get displayed. It seems the problem happens because MonthModeButtonView is a subview of MoodeButtonView, but why? Any idea about that?

Related

How to convert tableview cell to rounded corner with shadow in iOS 9.0 or later

I want to show tableview cell like above image.
Please help me out.
If you use normal table view cell
cell.contentView.layer.cornerRadius = 7.0f;
cell.contentView.layer.masksToBounds = YES;
If you use custom table view cell
cell.layer.cornerRadius = 7.0f;
cell.layer.masksToBounds = YES;
clear background color of UITableView in storyboard. Then you will be able to see the change.
For Cell Row Height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 44; //It default size.If you want to change to other size you can change.
}
To show rounded corner cell you can set corner radius.
For the current design you can create a custom UItableViewCell and set corner radius to it.
yourCustomCell.layer.cornerradius = 6.0f
In that UItableViewCell you need to add other labels as per your requirement.
Try this step you display image like:
you are add one view and top = 8, bottom = 8, left = 16, right = 16 set constraint in cell content view.
view.layer.cornerradius = 5.0f;
table background color set clear.
If you want rounded corners and shadow in an UITableviewCell you need to implement a (custom) UIView in the cell.
One way doing that is:
#interface customCell ()
#property (strong, nonatomic) IBOutlet UIView *someView;
#end
#implementation customCell
- (void)awakeFromNib {
[super awakeFromNib];
//Set the corner radius
self.someView.layer.cornerRadius = 5.0f;
// Add shadow
[self applyShadow];
}
- (void)applyShadow {
// Add shadow
[self.layer setShadowColor:[[UIColor blackColor] CGColor]];
[self.layer setShadowOffset:CGSizeMake(0, 2)];
[self.layer setShadowRadius:2.0];
[self.layer setShadowOpacity:0.5];
self.clipsToBounds = false;
self.layer.masksToBounds = false;
}
Add the custom View to the table View cell and set the below class:
CustomView.h
#import <UIKit/UIKit.h>
#import "Common.h"
#interface CustomView : UIView
{
IBInspectable UIColor *startColor;
IBInspectable UIColor *endColor;
}
#end
CustomView.m
#import "CustomView.h"
IB_DESIGNABLE
#implementation CustomView
- (void)drawRect:(CGRect)rect
{
CGContextRef context=UIGraphicsGetCurrentContext();
UIColor *ShadowColor=[UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1.0];
UIColor *backGround=[UIColor whiteColor];
CGFloat outerMagian=2.0;
CGRect outerRect=CGRectInset(self.bounds, outerMagian, outerMagian);
CGPathRef outerPath=createRoundedRectForRect(outerRect, 0.0);
CGContextSaveGState(context);
CGContextSetShadowWithColor(context, CGSizeMake(2, 2), 3.0, ShadowColor.CGColor);
CGContextSetFillColorWithColor(context, backGround.CGColor);
CGContextAddPath(context, outerPath);
CGContextFillPath(context);
CGContextRestoreGState(context);
CGFloat borderMarian=1.0;
CGRect borderRect=CGRectInset(self.bounds, borderMarian, borderMarian);
CGPathRef borderPath=createRoundedRectForRect(borderRect, 0.0);
CGContextSaveGState(context);
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:21.0/255.0 green:92.0/255.0 blue:136.0/255.0 alpha:1.0].CGColor);
CGContextAddPath(context, borderPath);
CGContextStrokePath(context);
CGContextRestoreGState(context);
CGPathRelease(outerPath);
CGPathRelease(borderPath);
UIBezierPath *path=[UIBezierPath bezierPathWithRoundedRect:outerRect cornerRadius:0.0];
[path addClip];
drawLinearGradient(context,outerRect, startColor.CGColor, endColor.CGColor);
}
#end
Common.h
#import <Foundation/Foundation.h>
#import <CoreGraphics/CoreGraphics.h>
#interface Common : NSObject
CGMutablePathRef createRoundedRectForRect(CGRect rect, CGFloat radius);
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor);
#end
Common.m
#import "Common.h"
#implementation Common
CGMutablePathRef createRoundedRectForRect(CGRect rect, CGFloat radius)
{
CGMutablePathRef path=CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMaxY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMaxX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMaxY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMaxY(rect), CGRectGetMinX(rect), CGRectGetMinY(rect), radius);
CGPathAddArcToPoint(path, NULL, CGRectGetMinX(rect), CGRectGetMinY(rect), CGRectGetMaxX(rect), CGRectGetMinY(rect), radius);
CGPathCloseSubpath(path);
return path;
}
void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor, CGColorRef endColor)
{
CGColorSpaceRef colorSpace=CGColorSpaceCreateDeviceRGB();
CGFloat locations[]={0.0,1.0};
NSArray *color=#[(__bridge id)startColor,(__bridge id)endColor];
CGGradientRef gradient=CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)color, locations);
CGPoint startPoint=CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint=CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
CGContextSaveGState(context);
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGContextRestoreGState(context);
CGGradientRelease(gradient);
CGColorSpaceRelease(colorSpace);
}
#end

How can my custom UIView draw a clear background?

I need to make my own UITableViewCellAccessoryDisclosureIndicator so I can have custom colors. I need to set the background to clear. No matter what I try, I can't get the clearColor to work. When I change the CGContextSetFillColorWithColor line below to redColor, it's red. I've tried setting opaque and backgroundColor and then calling super drawRect with my fill code removed and with my fill code present, but it's still no good. I've also tried CGContextClearRect in various ways.
Here's the red color version from the simulator. I need the UIView to be clear so that the pretty background image shows through.
I'm not terribly concerned with scrolling performance because this table will have very few rows.
- (instancetype) init {
self = [super init];
if (!self) return nil;
// [self setOpaque:NO];
// [self setBackgroundColor:[UIColor clearColor]];
return self;
}
- (void)drawRect:(CGRect)rect {
// [super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
// my obviously silly/naive attempt at making the background clear
CGContextSetFillColorWithColor(context, [UIColor clearColor].CGColor); // redColor works
CGContextFillRect(context, rect);
// drawing code for chevron
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 3.f);
CGContextSetLineJoin(context, kCGLineJoinMiter);
CGContextMoveToPoint(context, PADDING, PADDING);
CGContextAddLineToPoint(context, self.frame.size.width - PADDING, self.frame.size.height/2);
CGContextAddLineToPoint(context, PADDING, self.frame.size.height - PADDING);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
How about this?
self.contentView.opaque = NO;
self.backgroundColor = [UIColor clearColor];
Designated. Flippin. Initializers. What a waste of time.
UIView's designated initializer is initWithFrame. initWithFrame doesn't call init. Notice that I overrided init. I had assumed that initWithFrame would call init.
Here's what works (notice I don't have to paint the background even though I'm not calling super.drawRect):
- (instancetype) initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self) return nil;
// assume decent defaults
[self setColor:[UIColor darkGrayColor]];
[self setBackgroundColor:[UIColor clearColor]];
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetStrokeColorWithColor(context, [self color].CGColor);
CGContextSetLineWidth(context, 3.f);
CGContextSetLineJoin(context, kCGLineJoinMiter);
CGContextMoveToPoint(context, PADDING, PADDING);
CGContextAddLineToPoint(context, rect.size.width - PADDING, rect.size.height/2);
CGContextAddLineToPoint(context, PADDING, rect.size.height - PADDING);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}

CGContext clips more than it should

I have a UIView that has 3 custom subviews whose class is called (for now) LVStemp and that represent nodes in a tree. An LVStemp's frame is larger than the node itself because it will also include some drawing that lies outside the node (not coded yet). LVStemp has a property called nodeFrame that represents the frame of the node itself, in the main view's coordinates.
I'm trying to draw the nodes and fill them with a gradient. But the gradient is clipping in a way that is not what I intend. The result looks like this:
The top node is filled correctly but the bottom two are not.
Setting up, in the UIView's superview:
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 100, 350, 350)];
[self.view addSubview:myView];
LVStemp *node1 = [[LVStemp alloc] init];
node1.frame = CGRectMake(75, 0, 50, 50);
node1.nodeFrame = node1.frame;
node1.backgroundColor = [UIColor clearColor];
[myView addSubview:node1];
LVStemp *node2 = [[LVStemp alloc] init];
node2.frame = CGRectMake(0, 25, 100, 175);
node2.nodeFrame = CGRectMake(0, 150, 50, 50);
node2.backgroundColor = [UIColor clearColor];
[myView addSubview:node2];
LVStemp *node3 = [[LVStemp alloc] init];
node3.frame = CGRectMake(100, 25, 100, 175);
node3.nodeFrame = CGRectMake(150, 150, 50, 50);
node3.backgroundColor = [UIColor clearColor];
[myView addSubview:node3];
}
My code in LVStemp.h:
#interface LVStemp : UIView
#property (nonatomic) CGRect nodeFrame;
#end
My code in LVStemp.m:
#implementation LVStemp
- (void)drawRect:(CGRect)rect
{
// Build nodeBounds (= nodeFrame converted to self's coordinate system)
CGRect nodeBounds = [self convertRect:self.nodeFrame fromView:self.superview];
// Get CGContextRef
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw node with gradient
[self drawEllipseInRect:nodeBounds withGradient:context];
}
- (void)drawEllipseInRect:(CGRect)rect withGradient:(CGContextRef)context
{
UIGraphicsPushContext(context);
CGFloat colors[] = {
0.1, 0.8, 1.0, 1.0,
0.1, 0.4, 0.9, 1.0
};
CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(baseSpace, colors, NULL, 2);
CGColorSpaceRelease(baseSpace), baseSpace = NULL;
CGContextSaveGState(context);
CGContextAddEllipseInRect(context, rect);
CGContextClip(context);
CGPoint startPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMinY(rect));
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxX(rect));
CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
CGGradientRelease(gradient), gradient = NULL;
CGContextRestoreGState(context);
CGContextAddEllipseInRect(context, rect);
CGContextDrawPath(context, kCGPathStroke);
UIGraphicsPopContext();
}
#end
(drawEllipseInRect:withGradient: is adapted from here.)
And here's what happens if I comment out the clipping part:
//CGContextAddEllipseInRect(context, rect);
//CGContextClip(context);
Any suggestions?
The context is clipped correct, but you are drawing the gradient at the wrong Position:
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxX(rect));
Change that to use maxY:
CGPoint endPoint = CGPointMake(CGRectGetMidX(rect), CGRectGetMaxY(rect));
But as you might have noticed, the strokes are being clipped. That's because they are drawn centered on the given outline. You might consider calling it with some inset:
[self drawEllipseInRect:CGRectInset(nodeBounds, lineWidth/2, lineWidth/2) withGradient:context];

Draw a 5 pixel line on UIView [duplicate]

This question already has answers here:
What's the best approach to draw lines between views?
(2 answers)
Closed 9 years ago.
I have a very simple (well hopefully very simple) question. In Objective-C, how do you draw a line between two points and add it to a UIView? I have tried using a UIImageView and manipulating its Transform property, but that ends up turning the line into a square or a rectangle when using the following code:
[[self tline] setFrame:CGRectMake(start.x, start.y, width, 5)];
[[self tline] setTransform:CGAffineTransformMakeRotation(angle)];
I have two CGPoints, start and end, and I would like to draw a dynamic 5px line between the two points and add it to my subview.
BK:
The point start is the point where the user begins touching the screen, and the point end is the point where the user's finger is currently. Obviously this will move a lot during gameplay. I need to be able to move this line to connect these two points.
I am using the touchesBegan:, Moved:, and Ended: methods to create, move, and destroy the line.
CoreGraphics
I have the following code; how do I add this line to self.view?
CGContextRef c = UIGraphicsGetCurrentContext();
CGFloat color[4] = {1.0f, 1.0f, 1.0f, 0.6f};
CGContextSetStrokeColor(c, color);
CGContextBeginPath(c);
CGContextMoveToPoint(c, start.x, start.y);
CGContextAddLineToPoint(c, end.x, end.y);
CGContextSetLineWidth(c, 5);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextStrokePath(c);
Custom UIView:
#import <UIKit/UIKit.h>
#interface DrawingView : UIView
#property (nonatomic) CGPoint start;
#property (nonatomic) CGPoint end;
- (void)drawRect:(CGRect)rect;
#end
#import "DrawingView.h"
#implementation DrawingView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor); //change color here
CGFloat lineWidth = 5.0; //change line width here
CGContextSetLineWidth(context, lineWidth);
CGPoint startPoint = [self start];
CGPoint endPoint = [self end];
CGContextMoveToPoint(context, startPoint.x + lineWidth/2, startPoint.y + lineWidth/2);
CGContextAddLineToPoint(context, endPoint.x + lineWidth/2, endPoint.y + lineWidth/2);
CGContextStrokePath(context);
CGContextRestoreGState(context);
NSLog(#"%f",_end.x);
}
- (void)setEnd:(CGPoint)end
{
_end = end;
[self setNeedsDisplay];
}
#end
drawRect: is only called when I initialize the view...
Draw method in UIViewController:
- (void)drawTLine:(CGPoint)start withEndPoint:(CGPoint)end
{
[[self dview] setStart:start];
[[self dview] setEnd:end];
[[self dview] drawRect:[self dview].frame];
}
This is how I add the drawing view:
DrawingView* dview = [[DrawingView alloc] initWithFrame:self.view.frame];
[dview setBackgroundColor:[UIColor clearColor]];
[self.view addSubview:dview];
Subclass UIView and perform custom drawing using the drawRect: method
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGContextSetLineCap(context, kCGLineCapSquare);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor); //change color here
CGFloat lineWidth = 1.0; //change line width here
CGContextSetLineWidth(context, lineWidth);
CGPoint startPoint = rect.origin; //change start point here
CGPoint endPoint = {.x = CGRectGetMaxX(rect), .y = startPoint.y} //change end point here
CGContextMoveToPoint(context, startPoint.x + lineWidth/2, startPoint.y + lineWidth/2);
CGContextAddLineToPoint(context, endPoint.x + lineWidth/2, endPoint.y + lineWidth/2);
CGContextStrokePath(context);
CGContextRestoreGState(context);
}
This will draw a black 1px line at the top of your UIView.
If you need to update the line you can just use some properties like
#property (nonatomic) CGPoint startPoint;
and provide a custom implementation for the setter like
- (void)setStartPoint:(CGPoint)point {
_point = point;
[self setNeedsDisplay]; // this will cause drawRect: to be called again
}
Do that for every property that you wish to control and make your drawRect: use such properties for drawing.
you can create and UIBezierPath like this
UIBezierPath path = [[UIBezierPath alloc] init]
[path moveToPoint: CGPoint(x,y)] // X and Y, start point
[path addLineToPoint:CGPoint(x2,y2) // X2 and Y2, end point
if you want to create a shape you can put more points with addLineToPoint: method and finish use
closePath method
I hope help you

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