I am trying to draw a CAShapeLayer in a UIView and update the strokeStart and strokeEnd in response to a changing variable. The shape layer is created and drawn on screen as it should, but I am not able to update it.
My code:
//drawing shapeLayer on screen
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
shapeLayer = [CAShapeLayer layer];
UIBezierPath *variometerGuage = [UIBezierPath bezierPath];
[variometerGuage moveToPoint:CGPointMake(25, 5)];
[variometerGuage addLineToPoint:CGPointMake(25, 200)];
[shapeLayer setPath:variometerGuage.CGPath];
shapeLayer.strokeStart = 0;
shapeLayer.strokeEnd = .5;
shapeLayer.strokeColor = [UIColor colorWithWhite:0.5 alpha:1.0].CGColor;
shapeLayer.lineWidth = 10;
[shapeLayer setLineCap:kCALineCapButt];
[self.layer addSublayer:shapeLayer];
}
The following code is called from the View Controller. I know it is being executed, but the UIView is not updated.
- (void)updateVariometerGraphocsWithLift:(float)vario {
shapeLayer.strokeStart = vario; //vario being a number between 0 and 1
[shapeLayer setNeedsDisplay];
[self.layer addSublayer:shapeLayer];
[self.layer setNeedsDisplay];
[self.layer setNeedsLayout];
}
Any ideas?
Related
I have am UIImageView and I have made it to be circle with a width layer like in this image :
User can update the image and upload new one, I have a progress call back while image is uploaded. What I want is to animate the border with color while the image is uploaded, for example when user click upload the border start from top with green color and fill the width according to the progress.
I tried with this code:
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointZero radius:27 startAngle:-M_PI_2 endAngle:2 * M_PI - M_PI_2 clockwise:YES].CGPath;
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor greenColor].CGColor;
circle.lineWidth = 4;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 10;
animation.removedOnCompletion = NO;
animation.fromValue = #(0);
animation.toValue = #(1);
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[circle addAnimation:animation forKey:#"drawCircleAnimation"];
[imageView.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
[imageView.layer addSublayer:circle];
But It shows the circle in wrong position and this aproch does not take a progress, it's static is there any way to fill the border width of the layer of UIImageView according to progress ?
UIImageView is meant to show the images. I would not suggest changing it's behaviour. Even though you subclass the UIImageView and try to draw something in it, the drawRect of it will not get called. Let the imageView be imageView and use UIView for the rest
I suggest you to subclass the UIView and then draw a bezier path in it and animate the bezier path curve, You can later add the imageview to the uiview .
UIView subclass:
- (void)drawRect:(CGRect)rect {
[self drawImageHolderViewFrame:rect startAngle:_startAngle];
}
- (void)drawImageHolderViewFrame: (CGRect)frame startAngle: (CGFloat)startAngle
{
CGRect ovalRect = CGRectMake(CGRectGetMinX(frame) , CGRectGetMinY(frame) , frame.size.width-10, frame.size.height-10);
UIBezierPath* ovalPath = [UIBezierPath bezierPath];
[ovalPath addArcWithCenter: CGPointMake(CGRectGetMidX(ovalRect), CGRectGetMidY(ovalRect)) radius: CGRectGetWidth(ovalRect) / 2 startAngle: -91 * M_PI/180 endAngle: -startAngle * M_PI/180 clockwise: YES];
[UIColor.redColor setStroke];
ovalPath.lineWidth = 2;
[ovalPath stroke];
}
viewController class:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
_imageHolderView=[[MyView alloc]initWithFrame:CGRectMake(20, 20, 100, 100)];
_imageHolderView.startAngle=90;
_imageHolderView.backgroundColor=[UIColor clearColor];
[self.view addSubview: _imageHolderView];
[self startTImer];
}
-(void)startTImer{
NSTimer *timer=[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:#selector(animateBorder) userInfo:nil repeats:YES];
}
-(void)animateBorder{
if (_startAngle<=-270) {
_startAngle=90;
}
_startAngle--;
_imageHolderView.startAngle=_startAngle;
[_imageHolderView setNeedsDisplay];
}
This would give you:
Now you can add the imageview as a subview to the view you just created.
Please try the code below:
CAShapeLayer *circle = [CAShapeLayer layer];
circle.path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(29, 29) radius:27 startAngle:-M_PI_2 endAngle:2 * M_PI - M_PI_2 clockwise:YES].CGPath;
circle.fillColor = [UIColor clearColor].CGColor;
circle.strokeColor = [UIColor greenColor].CGColor;
circle.lineWidth = 4;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"strokeEnd"];
animation.duration = 10;
animation.removedOnCompletion = NO;
animation.fromValue = #(0);
animation.toValue = #(1);
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[circle addAnimation:animation forKey:#"drawCircleAnimation"];
[imageCircle.layer.sublayers makeObjectsPerformSelector:#selector(removeFromSuperlayer)];
[imageCircle.layer addSublayer:circle];
CGPoint startPoint = CGPointMake(50, 50);
CGPoint endPoint = CGPointMake(100, 100);
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
[bezierPath moveToPoint:startPoint];
[bezierPath addLineToPoint:endPoint];
layer = [CAShapeLayer layer];
//layer.frame = CGRectMake(50, 50, 100, 100);
layer.backgroundColor = [UIColor redColor].CGColor;
layer.path = bezierPath.CGPath;
layer.strokeColor = [UIColor greenColor].CGColor;
layer.fillColor = nil;
[self.view.layer addSublayer:layer];
NSLog(#"%#",NSStringFromCGRect(layer.bounds));
NSLog(#"%#",NSStringFromCGRect(layer.frame));
The Log Message that i get is 0,0 for both frame and bound yet the layer is drawn on the Screen.
I am confused on how it is possible that a layer can be drawn on screen in spite of having 0 on both bounds and frames. I though every view or layer needed to have these properties set in order to be displayed on screen.
I'm drawing a UIBezierPath into a CAShapeLayer, because I want to use strokeStart and strokeEnd to control which sections of the path are drawn. Everything works fine except lineDashPattern is being ignored. What am I doing wrong?
I have tried using setLineDash on the UIBezierPath itself. It works if I stroke the path directly into the context (using [path stroke]), but doesn't help if I assign the CGPath to the shapeLayer, which is what I need.
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
CAShapeLayer *shapeLayer = (CAShapeLayer *)layer;
UIBezierPath *path = [MyMapPath pathForMap:nil];
shapeLayer.path = path.CGPath;
shapeLayer.fillColor = [[UIColor clearColor] CGColor];
shapeLayer.strokeColor = [[UIColor redColor] CGColor];
shapeLayer.lineWidth = 6.0f;
shapeLayer.lineDashPattern = #[#2,#2];
shapeLayer.strokeStart = 0.0f;
shapeLayer.strokeEnd = 0.7f;
UIGraphicsPopContext();
}
I'm working on CAShapeLayer by creating a layer using CAShapeLayer like following:
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = CGRectMake(150, 50, 200, 200);
shapeLayer.fillColor = [UIColor whiteColor].CGColor;
shapeLayer.strokeColor = [UIColor orangeColor].CGColor;
[self.view.layer addSublayer:shapeLayer];
However, when I execute the codes and I cant see my shapeLayer in the simulator/device.
What I am missing here.
PS : If I am using CALayer *shapeLayer = [CALayer layer]; it works.Confused
add a path to it, like
shapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
A shaped layer needs a shape…
I put this in my View Controller and it works fine:
- (void)viewDidLoad
{
[super viewDidLoad];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = CGRectMake(150, 50, 200, 200);
shapeLayer.fillColor = [UIColor whiteColor].CGColor;
shapeLayer.strokeColor = [UIColor orangeColor].CGColor;
NSUInteger radius = 90;
shapeLayer.path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 2.0*radius, 2.0*radius)
cornerRadius:radius].CGPath;
[self.view.layer addSublayer:shapeLayer];
}
If you change the path like
shapeLayer.path = [UIBezierPath bezierPathWithRect:shapeLayer.bounds].CGPath;
it'll result in
Is there any way we can make only the selected area of a superview transparent? I have a UIView with black (alpha 0.5) background color. I am putting some hollow circles on the UIView and want the underneath UIView to be transparent so that I could see through it. Any idea
- (id)initWithFrame:(CGRect)iFrame andHollowFrames:(NSArray *)iHollowFrames withRadius:(NSNumber *)iRadius {
if ((self = [super initWithFrame:iFrame]) != nil) {
self.hollowFrames = [[NSSet setWithArray:iHollowFrames] allObjects];
self.hollowCircleRadius = iRadius;
[self addShadowView];
}
return self;
}
- (void)addShadowView {
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.bounds.size.width, self.bounds.size.height) cornerRadius:0];
for (NSValue *point in self.hollowFrames) {
UIBezierPath *circlePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(point.CGPointValue.x - self.hollowCircleRadius.floatValue, point.CGPointValue.y - self.hollowCircleRadius.floatValue, 2.0 * self.hollowCircleRadius.floatValue, 2.0 * self.hollowCircleRadius.floatValue) cornerRadius:self.hollowCircleRadius.floatValue];
[path appendPath:circlePath];
}
[path setUsesEvenOddFillRule:YES];
CAShapeLayer *fillLayer = [CAShapeLayer layer];
fillLayer.path = path.CGPath;
fillLayer.fillRule = kCAFillRuleEvenOdd;
fillLayer.fillColor = [UIColor blackColor].CGColor;
fillLayer.opacity = 0.5;
[self.layer addSublayer:fillLayer];
}
Here is a UIView subclass solution. In order for this to work you must leave the superviews backgroundColor property as nil witch is the default value. If this UIView-subclass is in the storyboard then make sure you set the background color to "Clear color" in the attributes inspector.
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIColor *blackBackgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5];
CGContextSetFillColorWithColor(ctx, blackBackgroundColor.CGColor);
CGContextFillRect(ctx, rect);
CGRect transparentPart = CGRectMake(10, 10, 120, 80);
CGContextClearRect(ctx, transparentPart);
}