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.
Related
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.
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.
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.
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.
I have this app using which one can draw basic shapes like rectangle, eclipse, circle, text etc.
I also allow free form drawing, which is stored as set-of-points, on the canvas.
Also a user can resize and move around these objects by operating on the selection handles that appear when an object is selected.
In addition the user should be able to zoom and pan the canvas.
I need some inputs on how to efficiently implement this drawing functionality.
I have following things in mind -
Use UIView's InvalidateRect and drawRect
Have a UIView for the main canvas and for each inserted object - invalidate the correspoding rect and redraw all the objects which intersects that rect in the drawRect function of the UIView.
Have a UIView and use CALayer ?
every one keep mentioning about the CALayer , I dont have much idea on this, before I venture into this I wanted a quick input on whether this route is worth taking.
like, https://developer.apple.com/library/ios/#qa/qa1708/_index.html
Have a UIImageView as canvas and when drawing each object, we do this
i) Draw the object into offscreen CGContext, basically, create a new CGContext by using UIGraphicsBeginImageContext, draw the shape, extract the image out of this CG context and use that as source of UIImageView's image property, but here how do I invalidate only a part of the UIImageView so that only that area gets refreshed.
Could you please suggest what is the best approach?
Is there any other efficient way to get this done?
Thanks.
Using a UIImage is more efficient for rendering multiple objects. But Using a CALayer is more efficient when moving and modifying a single object because you don't have to modify the other objects. So I think the best approach is to use a UIImage for general drawing and a CALayer for the shape that is being modified. In other words:
use a CALayer to draw the shape being added or modified, but don't draw it on the UIImage
use a UIImage to draw the other shapes
But OpenGL is still the most efficient solution, but don't bother with that if you don't have too many objects to draw.
If you want to draw polygons, you'll have to use Quartz framework, and have your drawing methods based on CALayer. It doesn't really matter which view you'll put your CALayers in, UIImageView or UIView. I'll say UIView since you won't be needing UIImageView's properties or methods for drawing.