Can a CAShapeLayer be passed to CGContextDrawLayerAtPoint? - ios

I've got a CAShapeLayer and was trying to draw it by passing it to CGContextDrawLayerAtPoint but get an error about passing a retainable parameter of CAShapeLayer *__strong to a function expecting a CGLayerRef.
I've done some browsing but can't figure out how to convert it/bridge the CAShapeLayer to the CGLayerRef.
If its not possible to convert, then can a CAShapeLayer be created using CGLayerCreateWithContext?
What I'm doing is creating a drawing with a UIBezierPath, then creating a CAShapeLayer and setting its path to the UIBezierPath then I want to display it. I could use addSublayer to display the shape layer however I'm going to display the same shape at multiple different points so rather than add multiple sublayers I was planning on using CGContextDrawLayerAtPoint to display it at different points.

Short answer: no.
Long answer: CAShapeLayer is a Core Animation layer (CALayer). You put it into your interface and it draws itself.
I know it's confusing, the way they use the word "layer", but CGContextDrawLayerAtPoint has nothing to do with that (it's using the word "layer" in a totally different way, referring to a CGLayer, which is completely different and is used utterly differently). You've gone down the wrong rabbit hole here.
I could use addSublayer: to display the shape layer
Not could. Must. That is what you do with a CAShapeLayer.
The alternative you are looking for would be to pull the CGPath out of the CAShapeLayer and assign that path and stroke it, multiple times in multiple places, in a CGContext. But there is really no need to do that. iOS drawing is all about layers; don't be afraid of having multiple sublayers.

Related

UIBezierPath fill vs CAShapeLayer

I'm trying to draw a simple shape as part of the interface of a game, and most tutorials use a UIBezierPath to define the path of a CAShapeLayer. However, I noticed that UIBezierPath already has an existing fill function.
This is probably a stupid question, but what is the difference between using that and using a separate CAShapeLayer to draw the path? Also, which one is better?
The "CA" in CAShapeLayer stands for CoreAnimation.
If you wish to animate, move, manipulate, or well, anything else to a UIBezierPath, you need to use a CALayer (or it's subclass CAShapeLayer).
UIBezierPaths are the correct thing to use if what you want is to simply "draw" - and it's usually done in draw(rect:). You can also "redraw" if you will.
But if you want persistence (and control) over a shape, turn that UIBezierPath into a CAShapeLayer.

How can I animate the drawing of a complex line shape in objective-c

So say I wanted to animate drawing this shape:
Animate it something like the disney logo which seems to "write" the letters.
Ideally, I'd also like to have control over how fast it was drawn (maybe just easing), how would I go about getting that done? Specifically imagine if that link had a line of varying widths.
Not too familiar with drawing things in objective-c so perhaps someone can help point me in the right direction in terms of strategy.
Create a CAShapeLayer and set that shape as its path. Use a CABasicAnimation or a CAKeyframeAnimation to animate its strokeEnd property.

Masking performance

I'm creating an animation that uncovers the underlying image. There's a virtual shape (e.g. star) moving chaotically and uncovering different parts of the image.
So I had two bitmaps so far:
mask (trace of a shape moving here'n'there)
image (underlying image)
So far in every drawRect() I was:
creating a newMask bitmap by copying the current mask
drawing a stamp on a newMask
creating a resulting bitmap (apply newMask onto an image)
drawing a resulting bitmap to screen context
I'm struggling with performance in this approach. Any ideas how to improve it?
In particular:
Is it possible to skip step 1. & 2. and draw onto mask directly (rather than clone it).
Should I start experimenting with CALayer approach (if this kind of masking is at all possible there)
Should I use OpenGL
Is there any other approach to tackle this?
No, you should not manipulate bitmaps. That is likely to be very CPU-intensive as well as jerky (not smooth animation.)
Instead you should use a CAShapeLayer as a mask and Core Animation.
With a shape layer you can install a path (a CGPath, which can be created easily from a UIBezierPath) into the layer. Then you create a CABasicAnimation that switches the path to a new path. The trick is to always keep the same number and type of control points in the starting and ending paths of the animation. (If the number and/or type of control points in the two paths are different you get very, very strange results. Note that the path calls that create arcs of circles actually generate different numbers of control points based on how much of a circle your arc covers, so circle arcs require special handling.)
I have a sample project on Github that demonstrates various Core Animation techniques, including a demonstration of a "clock wipe" animation that reveals/hides and image view much like you describe.
https://github.com/DuncanMC/iOS-CAAnimation-group-demo
The animation looks like this:
Note that the jerky nature of that image is because it's a GIF. The actual animation on a device is buttery-smooth. It's also possible to create very complex smooth animations like this one:
(That isn't a mask animation but it could be.)

What is the difference between CGContextAddLineToPoint and addLineToPoint function in UIBezierPath?

I have rendered some basic shapes using quartz-2d. I came across two methods to draw a line. First is to get a context using UIGraphicsGetCurrentContext() and then draw the line using CGContextAddLineToPoint.
And other way is to define a UIBezierPath object and draw using its function addLineToPoint
[bezierPath addLineToPoint:CGPointMake(10, 10)];
And then I have to add bezierPath to the context using CGContextAddPath.
So I wanted to know the difference between these two approaches as both are used to draw only a line. Is there a performance issue between these two? Also which method is better under what circumstances.
The UIBezierPath is an object from the UIKit, and allow you to use a set of function mixing particular curve with control point but also with a normal line.
Being an object from the UIKit you are not able to use CGContextAddLineToPoint, but anyway at the end you need to add the path in the context with CGContextAddPath.
With CGContexAddLineToPoint instead you draw directly on the context.
So my suggest is to use this last way if you have not particular reasons (like amazing curve with different control point). Otherwise, use UIBezierPath.
The CGContextAdd.. functions are lower-level C Quartz2D API that bridge between CGContext and CGPathRef instances. You use a CGContext when you want to draw something, while CGPathRef is the structure that manages geometric shapes.
On the other hand, UIBezierPath is an Objective-C UIKit class that wraps around CGPath, and provides also some bridge to CGContext functions, for example with setFill, setStroke, fill or stroke methods.
Is totally safe to mix the two approaches, but it's sure that CGContext gives you more tools. Performance and memory are better with direct CGContext calls, but performance difference are absolutely negligible in 99% of the cases.

Animating custom Bezier paths by looping drawRect

I have custom drawing code that uses bezier paths , gradients and strokes to perform my drawing. I want to run custom animations by looping drawRect and changing the values of properties on the bezier paths.
I have looked at using CAShapeLayer (UIView animation in drawrect) but that doesn't seem to cut it for me. My drawing code is quite complex, runs into a few hundred lines and all the drawing is done through bezier paths and gradients. Changing the drawings to a CAShapeLayer and then adding colors and gradients to it will be very time consuming!
I know that it is not recommended by Apple to explicitly call drawRect rather to use setNeedsDisplay to call draw rect (How to use DrawRect correctly). But the problem with doing that is i experience slight difference in the animation each time (very minute though). It may have something to do with the fact that setNeedsDisplay schedules drawRect to be called on the run loop but doesn't directly call it by itself.
I want to know what strategies i can use to use to loop drawRect and achieve synchronized perfectly timed animations each time. Is it possible to do this?
Both Animating Pie Slices Using a Custom CALayer and Animating Custom Layer Properties by Rob Napier are two good resources to learn how to make custom animations when you are doing completely custom drawing inside drawInContext:.
If you still feel that setting up an external mechanism to synchronize the drawing then I suggest that you look at CADisplayLink.

Resources