UICollectionViewCell dashed border - ios

I want to make my custom UICollectionViewCell have a dashed border around it, I have tried using layer and bezier path for it but none of the approach has helped.

In you UICollectionViewCell's Subclass
static CGFloat const kDashedBorderWidth = (2.0f);
static CGFloat const kDashedPhase = (0.0f);
static CGFloat const kDashedLinesLength[] = {4.0f, 2.0f};
static size_t const kDashedCount = (2.0f);
-(void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, kDashedBorderWidth);
CGContextSetStrokeColorWithColor(context, [UIColor blackColor].CGColor);
CGContextSetLineDash(context, kDashedPhase, kDashedLinesLength, kDashedCount) ;
CGContextAddRect(context, rect);
CGContextStrokePath(context);
}

Add this method in your class.
- (CAShapeLayer *) addDashedBorderWithColor: (CGColorRef)color withSize:(CGSize)size{
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
CGSize frameSize = size;
CGRect shapeRect = CGRectMake(0.0f, 0.0f, frameSize.width, frameSize.height);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake( frameSize.width/2,frameSize.height/2)];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:color];
[shapeLayer setLineWidth:5.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
[NSArray arrayWithObjects:[NSNumber numberWithInt:10],
[NSNumber numberWithInt:5],
nil]];
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
[shapeLayer setPath:path.CGPath];
return shapeLayer;
}
Write below line code in your cellForRowAtIndexPath method.
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier"#"cell"];
//Do what you want to set in your collectionViewCell
CAShapeLayer *maskLayer= [self addDashedBorderWithColor:[UIColor whiteColor].CGColor withSize:cell.borderView.frame.size];
cell.layer.mask = maskLayer;

Related

UIBezierPath - Rectangle with rounded corners

I'm using dr-Charts API to draw bar graphs in my application. I'm drawing the bar graph:
BarChart *barChartView = [[BarChart alloc] initWithFrame:CGRectMake(0, 150, WIDTH(self.view), HEIGHT(self.view) - 600)];
[barChartView setDataSource:self];
[barChartView setDelegate:self];
[barChartView setLegendViewType:LegendTypeHorizontal];
[barChartView setShowCustomMarkerView:TRUE];
[barChartView drawBarGraph];
[barChartView setDrawGridY:TRUE];
[barChartView setDrawGridX:FALSE];
[self.view addSubview:barChartView];
Actually, I want my bar chart rectangles to be rounded rect, something similar to this:
So, In BarChart.m, I started playing with API CAShapeLayer & UIBezierPath:
- (UIBezierPath *)drawBarPathWithStartPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint{
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y)];
//UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y) byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10, 10)];
[path stroke];
return path;
}
CAShapeLayer *shapeLayer = [[CAShapeLayer alloc] init];
[shapeLayer setPath:[[self drawBarPathWithStartPoint:endPoint endPoint:startPoint] CGPath]];
[shapeLayer setStrokeColor:[barData.barColor CGColor]];
[shapeLayer setFillColor:[barData.barColor CGColor]];
[shapeLayer setFillRule:kCAFillRuleEvenOdd];
[shapeLayer setLineWidth:0.5f];
[shapeLayer setOpacity:0.7f];
[shapeLayer setShadowRadius:0.0f];
[shapeLayer setShadowColor:[[UIColor clearColor] CGColor]];
[shapeLayer setShadowOpacity:0.0f];
[shapeLayer setValue:[barData.yAxisArray objectAtIndex:i] forKey:#"data"];
After following many answers on StackOverflow, I couldn't succeed.
I tried many answers like shapeLayer.cornerRadius = 10;
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(startPoint.x, startPoint.y, endPoint.x - startPoint.x, endPoint.y - startPoint.y) byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10, 10)];
Could you please help.
Try to draw wide line with rounded corners, for example
UIBezierPath *linePath = [UIBezierPath bezierPath];
[linePath moveToPoint:CGPointMake(topX, topY)];
[linePath addLineToPoint:CGPointMake(bottomX, bottomY)];
CAShapeLayer *line = [CAShapeLayer layer];
line.frame = some_frame;
line.lineCap = kCALineCapRound;
line.path = linePath.CGPath;
line.fillColor = nil;
line.lineWidth = 6.0;
line.cornerRadius = 3.0;
line.opacity = 1.0;
line.strokeColor = [UIColor greenColor].CGColor;
[line setMasksToBounds:YES];

How to create a circular dotted line as a CALayer?

I read this post Draw dotted (not dashed!) line, with IBDesignable in 2017 about drawing a dotted line (rather than a dashed line). However, I am not too familiar with graphics generally and I'm wondering how I can do this with a CALayer (so I don't have to do the whole get current graphics context thing).
I am trying to produce a dotted line that looks like this (the white part, ignore the background):
Here's the code I have working to produce a dotted line:
CAShapeLayer *shapelayer = [CAShapeLayer layer];
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
[path setLineCapStyle:kCGLineCapRound];
UIColor *fill = [UIColor whiteColor];
shapelayer.strokeStart = 0.0;
shapelayer.strokeColor = fill.CGColor;
shapelayer.lineWidth = 4.0;
shapelayer.lineJoin = kCALineJoinRound;
shapelayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:4],[NSNumber numberWithInt:6 ], nil];
shapelayer.path = path.CGPath;
return shapelayer;
How can I mirror the code in the SO post I referenced but continue using a CALayer?
I tried modifying the code from that post like so:
UIBezierPath * path = [[UIBezierPath alloc] init];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
[path setLineWidth:8.0];
CGFloat dashes[] = { path.lineWidth, path.lineWidth * 2 };
[path setLineDash:dashes count:2 phase:0];
[path setLineCapStyle:kCGLineCapRound];
[path stroke];
CAShapeLayer *returnLayer = [CAShapeLayer layer];
returnLayer.path = path.CGPath;
return returnLayer;
However, this ends up drawing nothing.
I hope this will help you.
CAShapeLayer *circleLayer = [CAShapeLayer layer];
[circleLayer setPath:[[UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 100, 100)] CGPath]];
circleLayer.lineWidth = 2.0;
[circleLayer setStrokeColor:[[UIColor redColor] CGColor]];
[circleLayer setFillColor:[[UIColor clearColor] CGColor]];
circleLayer.lineJoin = kCALineJoinRound;
circleLayer.lineDashPattern = [NSArray arrayWithObjects:[NSNumber numberWithInt:2],[NSNumber numberWithInt:3 ], nil];
// self.bgImage is parentView
[[self.bgImage layer] addSublayer:circleLayer];
self.bgImage.layer.borderWidth = 1.0f;
self.bgImage.layer.borderColor = [[UIColor blueColor]CGColor];
I have attached the output of the image
In my case I had to set the lineCapStyle rather than the lineJoinStyle.

How to connect buttons frame?

I have three buttons, all have different direction.
I have to connect them with a red line.
Just like Tic Tec Toc if game over than how to connect them with line:
How do I set line between them??
Or you can use this code..
UIView *lineView = [[UIView alloc] initWithFrame:CGRectMake(0, 200, self.view.bounds.size.width, 1)];
lineView.backgroundColor = [UIColor blackColor];
[self.view addSubview:lineView];
This is one way of drawing line :
Here you need to subclass UIView and then use CoreGraphics calls in the drawRect method:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 4.0);
CGContextSetStrokeColorWithColor(context, [UIColor blueColor].CGColor);
CGContextMoveToPoint(context, 25, 25);
CGContextAddLineToPoint(context, 75, 75);
CGContextStrokePath(context);
}
Another way is using UIBezierPath as follows :
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(10.0, 10.0)];
[path addLineToPoint:CGPointMake(100.0, 100.0)];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path CGPath];
shapeLayer.strokeColor = [[UIColor blueColor] CGColor];
shapeLayer.lineWidth = 3.0;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
[self.view.layer addSublayer:shapeLayer];

Adding inner shadow on uitextfield and uitextview

I am trying to add inner shadow on few UITextField and UITextView. I have set cornerRadius property for these controls to 1.0. and I am using the code which is mentioned below to create the inner shadow. But problem is that the shadow appears with the steep rectangular corners while the text view and the text field are with rounded corners.
I am calling following method in my viewcontroller class to add the shadow on my text field.
[self addInnerShadow:myTextField.frame];
- (void)addInnerShadow:(CGRect)frame
{
CAShapeLayer* shadowLayer = [CAShapeLayer layer];
[shadowLayer setFrame:frame];
[shadowLayer setShadowColor:[[UIColor blackColor] CGColor]];
[shadowLayer setShadowOffset:CGSizeMake(0.0f, 0.0f)];
[shadowLayer setShadowOpacity:1.0f];
[shadowLayer setShadowRadius:5];
[shadowLayer setFillRule:kCAFillRuleEvenOdd];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, CGRectInset(CGRectMake(0, 0, frame.size.width, frame.size.height),-5, -5));
CGMutablePathRef someInnerPath = CGPathCreateMutable();
CGPathAddRect(someInnerPath, NULL, CGRectInset(CGRectMake(0, 0, frame.size.width, frame.size.height), 0, 0));
CGPathAddPath(path, NULL, someInnerPath);
CGPathCloseSubpath(path);
[shadowLayer setPath:path];
CGPathRelease(path);
CAShapeLayer* maskLayer = [CAShapeLayer layer];
[maskLayer setPath:someInnerPath];
[shadowLayer setMask:maskLayer];
[[self.view layer] addSublayer:shadowLayer];
}
This
CGContextRef context = UIGraphicsGetCurrentContext();
// Shadow
UIColor* shadow4 = [[UIColor blackColor] colorWithAlphaComponent: 0.81];
CGSize shadow4Offset = CGSizeMake(0.1, 2.1);
CGFloat shadow4BlurRadius = 5;
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(20.5, 26.5, 200, 40) cornerRadius: 4];
[[UIColor whiteColor] setFill];
[rectanglePath fill];
CGRect rectangleBorderRect = CGRectInset([rectanglePath bounds], -shadow4BlurRadius, - shadow4BlurRadius);
rectangleBorderRect = CGRectOffset(rectangleBorderRect, -shadow4Offset.width, - shadow4Offset.height);
rectangleBorderRect = CGRectInset(CGRectUnion(rectangleBorderRect, [rectanglePath bounds]), -1, -1);
UIBezierPath* rectangleNegativePath = [UIBezierPath bezierPathWithRect: rectangleBorderRect];
[rectangleNegativePath appendPath: rectanglePath];
rectangleNegativePath.usesEvenOddFillRule = YES;
CGContextSaveGState(context);
{
CGFloat xOffset = shadow4Offset.width + round(rectangleBorderRect.size.width);
CGFloat yOffset = shadow4Offset.height;
CGContextSetShadowWithColor(context,
CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
shadow4BlurRadius,
shadow4.CGColor);
[rectanglePath addClip];
CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(rectangleBorderRect.size.width), 0);
[rectangleNegativePath applyTransform: transform];
[[UIColor grayColor] setFill];
[rectangleNegativePath fill];
}
CGContextRestoreGState(context);
[[UIColor blackColor] setStroke];
rectanglePath.lineWidth = 0.5;
[rectanglePath stroke];
Will produce this. You can manipulate this to get your desired effect.
Create path with UIBezierPath
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:frame
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(5.0, 5.0)];
And than use it with Layer.

(iOS) How to animate rounded rectangle with shapeLayer?

I am trying to animate the width of a rounded rectangle, the problem is when going from bigger width to thinner width, the animation does an "aberration ease jump".
Here's the code:
shapeLayer = [CAShapeLayer layer];
shapeRect = CGRectMake(0.0f, 0.0f, 150.0f, 200.0f);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake(iniPosX, 80.0f)];
[shapeLayer setFillColor:[[UIColor blackColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor clearColor] CGColor]];
[shapeLayer setLineWidth:1.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setOpacity:0.2];
path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
[shapeLayer setPath:path.CGPath];
[self.layer addSublayer:shapeLayer];
And when I start the animation:
- (void)adjustSelectorToPosAndSize:(float)posX andWidth:(float)width
{
shapeRect = CGRectMake(0.0f, 0.0f, width, 200.0f);
[shapeLayer setBounds:shapeRect];
[shapeLayer setPosition:CGPointMake(posX, 80.0f)];
path = [UIBezierPath bezierPathWithRoundedRect:shapeRect cornerRadius:15.0];
[shapeLayer setPath:path.CGPath];
}
What am I doing wrong?
Doing this with a CAShapeLayer seems overly complicated to me if you are only using it for a rounded rectangle. Why not simply use a CALayer with the cornerRadius properly set?
Animating the frame with cornerRadius set will work fine.
myLayer = [CALayer layer];
shapeRect = CGRectMake(0.0f, 0.0f, 150.0f, 200.0f);
[myLayer setBounds:shapeRect];
[myLayer setPosition:CGPointMake(iniPosX, 80.0f)];
[myLayer setBackgroundColor:[[UIColor blackColor] CGColor]];
[myLayer setBorderColor:[[UIColor clearColor] CGColor]];
[myLayer setBorderWidth:1.0f];
[myLayer setOpacity:0.2];
[myLayer setCornerRadius:15.0];
[self.layer addSublayer:myLayer];
Animating it is done by simply chaining the frame (not the path)
- (void)adjustSelectorToPosAndSize:(float)posX andWidth:(float)width
{
shapeRect = CGRectMake(0.0f, 0.0f, width, 200.0f);
[myLayer setBounds:shapeRect];
[myLayer setPosition:CGPointMake(posX, 80.0f)];
}
Important:
Some of the properties changed name like fillColor became backgroundColor and the strokeColor and lineWidth became borderColor and borderWidth etc.
Although CAShapeLayer’s path property is animatable, it does not do so implicitly like bounds and position, you have to make an explicit animation:
CABasicAnimation* anim = [CABasicAnimation animationWithKeyPath: #"path"];
anim.fromValue = (id) fromPath.CGPath;
anim.toValue = (id) toPath.CGPath;
anim.duration = 1.0;
[layer addAnimation: anim forKey: #“resize”];
Since you probably want all three animations to happen together, you can make them all explicit and grouped together:
CABasicAnimation* anim1 = [CABasicAnimation animationWithKeyPath: #"position"];
anim1.fromValue = [NSValue valueWithCGPoint: fromPosition];
anim1.toValue = [NSValue valueWithCGPoint: toPosition];
anim1.duration = 1.0;
CABasicAnimation* anim2 = [CABasicAnimation animationWithKeyPath: #"bounds"];
anim2.fromValue = [NSValue valueWithCGRect: fromBounds];
anim2.toValue = [NSValue valueWithCGRect: toBounds];
anim2.duration = 1.0;
CABasicAnimation* anim3 = [CABasicAnimation animationWithKeyPath: #"path"];
anim3.fromValue = (id) fromPath.CGPath;
anim3.toValue = (id) toPath.CGPath;
anim3.duration = 1.0;
CAAnimationGroup* animG = [CAAnimationGroup animation];
animG.animations = [NSArray arrayWithObjects: anim1, anim2, anim3, nil];
animG.duration = 1.0;
[layer addAnimation: animG forKey:#"resize"];
If your shape does not have contents or children then you may be able to use a CALayer instead:
CALayer* rectLayer = [CALayer layer];
rectLayer.masksToBounds = YES;
rectLayer.bounds = startBounds;
rectLayer.backgroundColor = [[UIColor blackColor] CGColor];
rectLayer.cornerRadius = 15.0;
[self.layer addSublayer: rectLayer];
// Then later implicitly animate the bounds:
rectLayer.bounds = newBounds;

Resources