Creating a triangle shape in a UIButton - ios

I am trying to create a button with a triangle icon/shape in it. I have created the triangle in Paintcode then copied the beizer path and added it to the button layer as below.
-(void)setupShowHideRouteButton {
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(10.5, 8.5)];
[bezierPath addCurveToPoint: CGPointMake(40.5, 8.5) controlPoint1: CGPointMake(39.5, 8.5) controlPoint2: CGPointMake(40.5, 8.5)];
[bezierPath addLineToPoint: CGPointMake(26.39, 22.3)];
[bezierPath addLineToPoint: CGPointMake(25.2, 23.5)];
[bezierPath addLineToPoint: CGPointMake(10.5, 8.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.showHideRouteViewBtn.bounds;
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
[self.showHideRouteViewBtn.layer addSublayer:shapeLayer];
}
However, this does not appear to work. I am setting the background color of the UIButton so I know the frame is correct and outlet working as expected it is just not adding the shape?
Second Approach
UIBezierPath* bezierPath = UIBezierPath.bezierPath;
[bezierPath moveToPoint: CGPointMake(10.5, 8.5)];
[bezierPath addCurveToPoint: CGPointMake(40.5, 8.5) controlPoint1: CGPointMake(39.5, 8.5) controlPoint2: CGPointMake(40.5, 8.5)];
[bezierPath addLineToPoint: CGPointMake(26.39, 22.3)];
[bezierPath addLineToPoint: CGPointMake(25.2, 23.5)];
[bezierPath addLineToPoint: CGPointMake(10.5, 8.5)];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
// Create a mask layer and the frame to determine what will be visible in the view.
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
CGRect maskRect = self.showHideRouteViewBtn.bounds;
// Create a path with the rectangle in it.
CGPathRef path = CGPathCreateWithRect(maskRect, NULL);
// Set the path to the mask layer.
maskLayer.path = bezierPath.CGPath;
// Release the path since it's not covered by ARC.
CGPathRelease(path);
// Set the mask of the view.
self.showHideRouteViewBtn.layer.mask = maskLayer;
This did not work either.

Try the following:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.showHideRouteViewBtn.bounds;
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 2;
self.showHideRouteViewBtn.layer.mask = shapeLayer;

Related

Animation for Layer in iOS

I masked an image with BezierPath.
I want to animate that mask image.
My code is as follow:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(84.95, 205.11)];
[bezierPath addCurveToPoint: CGPointMake(122.89, 232.14) controlPoint1: CGPointMake(84.95, 205.11) controlPoint2: CGPointMake(110.81, 223.56)];
[bezierPath addCurveToPoint: CGPointMake(124.33, 233.04) controlPoint1: CGPointMake(123.37, 232.46) controlPoint2: CGPointMake(123.85, 232.78)];
[bezierPath closePath];
CAShapeLayer *maskImage = [CAShapeLayer layer];
maskImage.path = bezierPath.CGPath;
maskImage.fillColor = [[UIColor blackColor] CGColor];
_myImageView.layer.mask = maskImage;
CAShapeLayer *border = [CAShapeLayer layer];
border.path = bezierPath.CGPath;
border.strokeColor = [UIColor redColor].CGColor;
border.fillColor = [[UIColor clearColor] CGColor];
border.lineWidth = 5;
[_myImageView.layer addSublayer:border];
How can I animate this?
Thank you
Use following piece of code:
#property (nonatomic, retain) CAShapeLayer *animationLayer;
- (IBAction)drawPattern:(id)sender{
[self setup];
[self.animationLayer removeAllAnimations];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 10.0;
pathAnimation.fromValue = #(0);
pathAnimation.toValue = #(1);
[self.animationLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
}
- (void)setup{
[self.animationLayer removeFromSuperlayer];
self.animationLayer = nil;
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(84.95, 205.11)];
[bezierPath addCurveToPoint: CGPointMake(122.89, 232.14) controlPoint1: CGPointMake(84.95, 205.11) controlPoint2: CGPointMake(110.81, 223.56)];
[bezierPath addCurveToPoint: CGPointMake(124.33, 233.04) controlPoint1: CGPointMake(123.37, 232.46) controlPoint2: CGPointMake(123.85, 232.78)];
CAShapeLayer *pathLayer = [CAShapeLayer layer];
// provide frame & bounds so that path can be drawn over that.
pathLayer.frame = CGRectMake(35, 100, 250, 200);
pathLayer.bounds = CGRectMake(35, 100, 250, 200);
pathLayer.path = bezierPath.CGPath;
pathLayer.strokeColor = [UIColor redColor].CGColor;
pathLayer.fillColor = [[UIColor clearColor] CGColor];
pathLayer.lineWidth = 6.f;
pathLayer.lineJoin = kCALineJoinRound;
[self.view.layer addSublayer:pathLayer];
[self setAnimationLayer:pathLayer];
}
Please let me know if this works for you & also if anything else comes up.
At the end of your code try below one
CAShapeLayer *pathLayer;
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [aPath CGPath];
shapeLayer.strokeColor = [[UIColor greenColor] CGColor];
shapeLayer.fillColor = nil;
shapeLayer.lineWidth = 5.0f;
shapeLayer.lineJoin = kCALineJoinBevel;
[myImageView.layer addSublayer:shapeLayer];
pathLayer = shapeLayer;
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
pathAnimation.duration = 3.0;
pathAnimation.fromValue = #(0.0f);
pathAnimation.toValue = #(1.0f);
[pathLayer addAnimation:pathAnimation forKey:#"strokeEnd"];
We can animate the image with path using CABasicAnimation.
CABasicAnimation has position,path,transform,opacity,shadow......which including all layers keypaths,shape layer keypaths,CA layer keypaths,Text layer keypaths,Mask layer keypaths,UIView layer keypaths,Effect,Emit and replicator keypaths.
According to your question for drawing path we can use path and strokeEnd.But most prepare strokeEnd for accessing shape style properties.
So I think strokeEnd is the best one for animating the mask image with bezier path.
Below I give the code for path and strokeEnd also I diffrecicate both.
If I use animationWithKeyPath is path
The shape to be rendered and animatable.It is specifying the shape path.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"path"];
animation.fromValue = #(0);
animation.toValue = #(1);
animation.duration = 6.0f;
[maskImage addAnimation:animation forKey:#"animatePath"];
The Path is a collection of individual components like lines and arcs that can be drawn and also animated.It is fundamental concept of Quartz 2D.
In below code I use strokeEnd
Generally if we want to access the shape style properties we can use
strokeEnd
The relative location at which to stop stroking the path and animatable.
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.fromValue = #(0);
animation.toValue = #(1);
animation.duration = 6.0f;
[maskImage addAnimation:animation forKey:#"strokeEnd"];
The value of this property must be in the range 0.0 to 1.0. The default value of this property is 1.0.
Also strokeEnd defines following thing
Combined with the strokeStart property, this property defines the subregion of the path to stroke. The value in this property indicates the relative point along the path at which to finish stroking while the strokeStart property defines the starting point.

Draw border around a polygon

-(void)setTopRightCornerWithRadious:(CGFloat)radious View:(UIView*)vw
{
UIGraphicsGetCurrentContext();
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:vw.bounds byRoundingCorners:UIRectCornerTopRight cornerRadii:CGSizeMake(radious, radious)];
[maskPath closePath];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = vw.bounds;
maskLayer.path = maskPath.CGPath;
vw.layer.mask=maskLayer;
if (vw.layer.borderColor) {
UIColor *color=[UIColor colorWithCGColor:vw.layer.borderColor];
[color setStroke];
maskLayer.accessibilityPath.lineWidth=1.0f;
[maskLayer.accessibilityPath stroke];
}
}
-(void)setAllBorderForView:(UIView*)vw Color:(UIColor*)color Thickness:(CGFloat)thick
{
if (vw) {
vw.layer.borderWidth=thick;
vw.layer.borderColor=color.CGColor;
}
}
I wish to draw a border surrounded by these two button. I tried so many times with CAShapeLayer and UIBezierPath, but failed, may be I missed some thing. Some of them solve this problem using UIView, but I don't want that. I only want to solve the issues only by using the CAShapeLayer and/or UIBezierPath.
Here is my smaple code... WHere is my fault???? At very first i set the border, then I tried to set the corner. For few time may the border color exist or may not exist.
If you just need to have a border around the button, add stroke to the button bezier path.
- (void)drawRect: (CGRect)frame
{
UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(x,y,widht,height) byRoundingCorners: UIRectCornerTopRight cornerRadii: CGSizeMake(17.25, 17.25)];
[rectangle2Path closePath];
[UIColor.grayColor setFill];
[rectangle2Path fill];
[UIColor.redColor setStroke];
rectangle2Path.lineWidth = 1;
[rectangle2Path stroke];
}
Or If you want to have space between the border and the button bezier path, then you should add two bezier paths. One for button, and another one for border.
- (void)drawRect: (CGRect)frame
{
UIBezierPath* rectanglePath = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(CGRectGetMinX(frame), CGRectGetMinY(frame), floor((CGRectGetWidth(frame)) * 1.00000 + 0.5), floor((CGRectGetHeight(frame)) * 1.00000 + 0.5)) byRoundingCorners: UIRectCornerTopRight cornerRadii: CGSizeMake(28, 28)];
[rectanglePath closePath];
[UIColor.redColor setStroke];
rectanglePath.lineWidth = 1;
[rectanglePath stroke];
UIBezierPath* rectangle2Path = [UIBezierPath bezierPathWithRoundedRect: CGRectMake(CGRectGetMinX(frame) + 7, CGRectGetMinY(frame) + 8, 103, 62) byRoundingCorners: UIRectCornerTopRight cornerRadii: CGSizeMake(26, 26)];
[rectangle2Path closePath];
[UIColor.grayColor setFill];
[rectangle2Path fill];
}
In your custom button class you set this:
UIBezierPath outerPAth= [UIBezierPath bezirePath];
[[UIColor WhiteColor] setStroke];
outlinePath.lineWidth=5.0;
[outlinePath stroke];

How to add an arrow in UIImageView?

I have to an arrow in a UIImageView. it may act like a transparent space in UIImageView. a sample image is attached and highlighted with yellow box.
Use mask,for example you have a 200*100 image
Code
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 0)];
[bezierPath addLineToPoint:CGPointMake(0,100)];
[bezierPath addLineToPoint:CGPointMake(10,100)];
[bezierPath addLineToPoint:CGPointMake(20,90)];
[bezierPath addLineToPoint:CGPointMake(30, 100)];
[bezierPath addLineToPoint:CGPointMake(200,100)];
[bezierPath addLineToPoint:CGPointMake(200, 0)];
[bezierPath closePath];
shapeLayer.path = bezierPath.CGPath;
self.imageview.layer.mask = shapeLayer;
Note:The clip part is can still catch touch event or gesture as part of Imageview,you may rewrite pointInside to Ignore touch event
Edit About how it works
You just need to create any shape you like ,the about code I just create a shape like this,then use this shape to set mask
So,if you want make a right arrow,just create a shape like this
CAShapeLayer * shapeLayer = [CAShapeLayer layer];
UIBezierPath * bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:CGPointMake(0, 0)];
[bezierPath addLineToPoint:CGPointMake(0,100)];
[bezierPath addLineToPoint:CGPointMake(170,100)];
[bezierPath addLineToPoint:CGPointMake(180,90)];
[bezierPath addLineToPoint:CGPointMake(190, 100)];
[bezierPath addLineToPoint:CGPointMake(200,100)];
[bezierPath addLineToPoint:CGPointMake(200, 0)];
[bezierPath closePath];
shapeLayer.path = bezierPath.CGPath;
Shape screenshot
So,what you need is
Figure out what shape you want(the outline mask shape)
Use bezierPath to draw
Set mask

add UIBezierPath to UIView

i have a UIView with layout constraint (width:400, height:180) and i want to add a bezier inside the view.
my bezier is this:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// origin, height | left border
[bezierPath moveToPoint: CGPointMake(0, 20)];
// origin, height | left border
[bezierPath addLineToPoint: CGPointMake(0, _donationView.frame.size.height)];
// height, width | bottom border
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width , _donationView.frame.size.height)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width, 20)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width - 20, 20)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width - 30, 15)];
[bezierPath addLineToPoint: CGPointMake(_donationView.frame.size.width - 40, 20)];
[bezierPath addLineToPoint: CGPointMake(0, 20)];
[bezierPath closePath];
[UIColor.grayColor setFill];
[bezierPath fill];
[UIColor.blackColor setStroke];
bezierPath.lineWidth = 1;
[bezierPath stroke];
after creating bezier i pass to:
CAShapeLayer *shapeView = [[CAShapeLayer alloc] initWithLayer:_donationView.layer];
[shapeView setPath:bezierPath.CGPath];
_donationView.layer.mask = shapeView;
but the bezier goes to this CGRect(0, 0, 240, 128);
how can i have a CGRect of (0,0,400, 180)?
thanks guys
The problem is that you're doing this too soon, before donationView has attained its final size. Thus, when you read off its frame.size, you are getting the wrong result. You need to postpone the addition of the mask until after layout has taken place.
You can also run into further issues if donationView is ever resized later. It is quite annoying that a mask layer (or any layer) doesn't get to participate in autolayout. So if donationView is resized, you will have to remove this mask and create a new mask with the correct size and add it again.

Change direction in stroke animation

I want to revert the direction of a path drawing. So I changed fromValue and toValue propeties, but now full path is visible at the start of animation and it begins to disappear.
I have the path:
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint: CGPointMake(213.79, 170.83)];
[bezierPath addCurveToPoint: CGPointMake(131.93, 95) controlPoint1: CGPointMake(212.14, 128.68) controlPoint2: CGPointMake(176.13, 95)];
[bezierPath addCurveToPoint: CGPointMake(50, 173.85) controlPoint1: CGPointMake(86.68, 95) controlPoint2: CGPointMake(50, 130.3)];
[bezierPath addCurveToPoint: CGPointMake(131.93, 252.7) controlPoint1: CGPointMake(50, 217.4) controlPoint2: CGPointMake(86.68, 252.7)];
[bezierPath addCurveToPoint: CGPointMake(157.55, 248.76) controlPoint1: CGPointMake(140.88, 252.7) controlPoint2: CGPointMake(149.49, 251.32)];
[bezierPath addCurveToPoint: CGPointMake(209.69, 281) controlPoint1: CGPointMake(166.59, 267.78) controlPoint2: CGPointMake(186.54, 281)];
[bezierPath addCurveToPoint: CGPointMake(267, 225.85) controlPoint1: CGPointMake(241.34, 281) controlPoint2: CGPointMake(267, 256.31)];
[bezierPath addCurveToPoint: CGPointMake(213.79, 170.83) controlPoint1: CGPointMake(267, 196.71) controlPoint2: CGPointMake(243.53, 172.86)];
[bezierPath closePath];
[color0 setStroke];
bezierPath.lineWidth = 11;
[bezierPath stroke];
And the animation:
UIColor* color = [UIColor colorWithRed: 0.369 green: 0.42 blue: 0.475 alpha: 1];
UIBezierPath *bezierPath = [self bezierPath];
CAShapeLayer *bezier = [[CAShapeLayer alloc] init];
bezier.path = bezierPath.CGPath;
bezier.lineCap = kCALineCapRound;
bezier.strokeColor = color.CGColor;
bezier.fillColor = [UIColor clearColor].CGColor;
bezier.strokeStart = 1.0;
bezier.strokeEnd = 0.0;
bezier.lineWidth = 8.0;
bezier.strokeStart = 0.0;
bezier.strokeEnd = 1.0;
[self.view.layer addSublayer:bezier];
if (animate)
{
CABasicAnimation *animateStrokeEnd = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animateStrokeEnd.duration = appDelegate.myPlayer.audioPlayer.duration;
animateStrokeEnd.fromValue = [NSNumber numberWithFloat:1.0f];
animateStrokeEnd.toValue = [NSNumber numberWithFloat:0.0f];
[bezier addAnimation:animateStrokeEnd forKey:#"strokeEndAnimation"];
}
You were on the right track but you should have animated the strokeStart property instead.
Start with having both strokeStart and strokeEnd as 1.0.
Then animate strokeStart from 1.0 to 0.0. This will give you the effect of the path being stroked from the end to the start.
For example when 25% of the path is stoked you will have strokeStart at 0.75 and strokeEnd at 1.0.

Resources