How to rotate a CAShapeLayer along with its Pattern? - ios

I have a CAShapeLayer, I am able to rotate it using UIRotationGestureRecognizer. But when rotating, it only rotate its frame not the filled pattern inside it.
CAShapeLayer *shapeLayer = [finalShapeLayersArray objectAtIndex:indextobeRotated];
CGPathRef path = createPathRotatedAroundBoundingBoxCenter(shapeLayer.path,recognizer.rotation);
shapeLayer.path = path;
CGPathRelease(path);
recognizer.rotation = 0;
Bu doing this, it only rotates the boundary of the CAShapeLayer. I have filled a pattern with an image. I just want that pattern should also be rotated.

You need to set a rotation transform on your layer using CALayer's transform property instead (you do not rotate the the shape path then as the whole layer will be rotated).
See this answer on how to create such a transform. Also see the Core Animation Function Reference for all the functions that can create/modify transformations.

Related

Drag and sacale a rectangle drawn using UIBezierPath IOS/Swift

I am developing a program which identifies a rectangle in an image and draws a path on the border of that identified rectangle. Now I want to reposition that path in case if it is not on the exact position. For an example look at this image
In cases like this i need to drag the corners of the path and reposition it as it fits the rectangle.
To draw the path I used CAShapeLayer and UIBezierPath. Here is the code I used to draw the path.
// imgView is the UIImageView which contains the image with the rectangle
let line: CAShapeLayer = CAShapeLayer();
line.frame = imgView.bounds;
let linePath: UIBezierPath = UIBezierPath();
linePath.moveToPoint(CGPointMake(x1, y1);
linePath.addLineToPoint(CGPointMake(x2, y2);
linePath.addLineToPoint(CGPointMake(x3, y3);
linePath.addLineToPoint(CGPointMake(x4, y4);
linePath.addLineToPoint(CGPointMake(x1, y1);
linePath.closePath();
line.lineWidth = 5.0;
line.path = linePath.CGPath;
line.fillColor = UIColor.clearColor().CGColor;
line.strokeColor = UIColor.blueColor().CGColor;
imgView.layer.addSublayer(line);
The thing is I tried to add a gesture to UIBezierPath. But there is nothing like that as I noticed. Couldn't find anything regarding this. So can someone please let me know a way to get my work done. Any help would be highly appreciated.
You are right that there is no way to attach a gesture recognizer to a UIBezierPath. Gesture recognizers attach to UIView objects, and a UIBezierPath is not a view object.
There is no built-in mechanism to do this. You need to do it yourself. I would suggest building a family of classes to handle it. Create a rectangle view class. It would use a bezier path internally, as well as placing 4 corner point views on the vertexes and installing pan gesture recognizers each corner point view.
Note that Cocoa rectangles (CGRects) can't be rotated. You'll need to use a series of connected line segments and write logic that forces it to stay square.

Change fill color of a part of a combined path with animation?

In simple words only part of my icon must change color at some event.
I drawing an icon with curves inside an UIView. The drawing boilds
down to combining several paths together like this (swift code):
let combinedPath = CGPathCreateMutableCopy(bezierPath.CGPath)
CGPathAddPath(combinedPath, nil, bezier2Path.CGPath);
CGPathAddPath(combinedPath, nil, bezier3Path.CGPath);
Then I'm adding the combined path to the UIView like this (obj-c, sorry for
the mix):
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.frame = self.bounds;
shapeLayer.path = combinedPath;
self.layer.mask = shapeLayer;
And now at some point in time I want to change the fill color of
only one bezierPath with animation. How can I do this?
You can clean the current mask, and redraw any of the bezierpath with stroke/fill color and combine them together, and set it again. And for drawing animation, you can refer to How to Animate CoreGraphics Drawing of Shape Using CAKeyframeAnimation

Animate CALayer mask change with fade effect

I have a UIView, with view.layer.mask set to an instance of CAShapeLayer. The shape layer contains a path, and now I want to add a hole to this shape by adding a second shape with even/odd rule, and fade-in the appearance of this hole.
The problem is that adding to path doesn't seem to be animatable:
[UIView animateWithDuration:2 animations:^() {
CGPathAddPath(parentPath, nil, holePath);
[v.layer.mask didChangeValueForKey:#"path"];
}];
How would I animate this?
After some fiddling, found a workaround:
Create a layer with two sublayers with two desired shapes, and use it as a mask
Animate opacity of the first sublayer (without a hole) from 1 to 0.
This works because child CAShapeLayer instances appear to be used as a union. When you hide the first sublayer without a hole, only the hole will be uncovered, the shared area will not change.
CGMutablePathRef p = CGPathCreateMutable();
// First path
CGPathAddPath(p, nil, outerPath);
CAShapeLayer *mask1 = [CAShapeLayer layer];
mask1.path = p;
// 2nd path with a hole
CGPathAddPath(p, nil, innerPath);
CAShapeLayer *mask2 = [CAShapeLayer layer];
mask2.path = p;
mask2.fillRule = kCAFillRuleEvenOdd;
CGPathRelease(p);
// Create actual mask that hosts two submasks
CALayer *mask = [CALayer layer];
[mask addSublayer:mask1];
[mask addSublayer:mask2];
myView.layer.mask = mask;
mask.frame = v.layer.bounds;
mask1.frame = mask.bounds;
mask2.frame = mask.bounds;
// ...
// Hide mask #1
CABasicAnimation *a = [CABasicAnimation animationWithKeyPath:#"opacity"];
a.fromValue = #1.0;
a.toValue = #0.0;
a.duration = 1.0;
a.fillMode = kCAFillModeForwards; // Don't reset back to original state
a.removedOnCompletion = NO;
[mask1 addAnimation:a forKey:#"hideMask1"];
You can't use UIView animation to animate CALayers.
Most layer property changes do animation by default (implicit animation). As I recall, shape layer path changes are an exception to that.
You'll need to create a CAAnimation object where the property you are animating is the path on your mask layer.
However, that probably won't give the effect you want. The reason is that when you change a path on a shape layer, Core Animation tries to animate the change in shape of the path. Furthermore, path changes only work properly when the starting and ending paths have the same number and type of control points.
I'm not sure how you'd achieve a cross-fade between 2 different masks without a lot of work.
Off the top of my head, the only way I can think of to do this would be to create a snapshot of the new view appearance with the changed mask (probably using Core Image filters) and then do a cross-fade of a layer that displays that snapshot. Once the crossfade is complete, you would install the new path in your mask layer without animation and then remove the snapshot bitmap, revealing the real view underneath.
There might be a simpler way to achieve what you're after but I don't know what that would be. Maybe one of the CA experts that contributes to SO could chime in here.

core gore graphics and alpha values

I am draw some circle on a map with
CGContextFillEllipseInRect
This works great, but the only issue is that I have the alpha value set to .5
If tow or more circle overlap the the overlap area is darker because of the two alpha values being added I suppose. Is there an easy way to maintain the same alpha value regardless if the circle overlap?
Thanks
If you're working with closed shapes and drawing them all in the same drawrect (or at least in the same context) you can accomplish what you want by using UIBezierPath.
// create a path with a circle
UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(50, 50) radius:50.0f startAngle:0 endAngle:M_PI * 2.0f clockwise:YES];
// add another circle to the path
[path appendPath:[UIBezierPath bezierPathWithArcCenter:CGPointMake(100, 100) radius:50.0f startAngle:0 endAngle:M_PI * 2.0f clockwise:YES]];
// draw them both, since they are in a single path they will be treated as a single shape
[path fill];
Draw the circles at full opacity, and set the opacity of the view/layer to 0.5.
EDIT:
To clarify, I assume you know how to stop drawing your circles at half opacity, since you managed to set it up that way in the first place. So make the opacity of your circles be 1.0. Then, set your view's opacity to 0.5 by doing the following:
view.alpha = 0.5;
I'm assuming you have a UIView subclass, so this can either be in the place in your code where you create the view or it could be inside the subclass in your init method for it. So if you're doing it from within the UIView subclass, it would be self instead of view:
self.alpha = 0.5;
Also, this will make all of the drawing you do in this view be 0.5 opacity. If you want to have some content that's full opacity and some that's half opacity, the simplest solution I can give you is to have two different views, one for the half opacity content, the other for the full opacity content. There are better ways to accomplish this, but for the sake of communicating easily a solution, this is probably easiest.

Drawing custom shape and animating custom properties using CALayers?

I have a custom shape that i want to draw using UIBezierPaths and i want to use this drawing as a CALayer inside my view. The bezier path i use works if i draw it directly onto the UIView (inside drawRect). I want to know as to how i can use the same bezier drawing and perform the drawing inside my CALayer. I would then add this layer as a sub layer inside my view!
For instance lets say I'm drawing a concentric circle with my bezier paths and i want to draw this using a CALayer, how would i go about animating custom properties of the path, like its center, radius, startAngle and endAngle?
Specifically i want to know how i should
Arrange my CALayer (initializing, drawing , upadte drawing ,etc)
How do i draw the bezier inside my CALayer?
How do i interact between the layer and the view that contains it?
Any help is appreciated!
Here's how you create & draw a UIBezierPath in a CALayer. Using a CAShapeLayer is the easiest way:
UIBezierPath *circlePath = [UIBezierPath bezierPathWithOvalInRect:...];
CAShapeLayer *circleLayer = [CAShapeLayer layer];
circleLayer.path = circlePath.CGPath;
[self.view.layer addSublayer:circleLayer];
Here's a good tutorial on creating animatable properties in CALayers.

Resources