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.
Related
I have a UIView subclass that does some drawing, and at one point calls CGContextSetLineDash to set the dash and gap information on the line stroke.
Is it possible to do this with an animation? If so, how? I find Core Graphics very confusing and I'm admittedly just getting started out with it.
Quick diagrams! I'm trying to implement this:
I have this working almost 100% currently (though this is a slightly different stage of the animation):
Everything looks good minus the fade effects at the end of the stroke. Is that possible using a simple CGPath? I'm animating strokeStart and strokeEnd to get the current effect. I've tried using CAGradientLayer as a mask on the layer, but that adds a gradient over the entire layer, not just the ends. Overriding drawRect isn't possible since I'm doing this dynamically with animations.
Any thoughts about how to achieve this effect? I have no idea which direction to go.
Depending on how important opacity is and how complex the rest of the animation is, one option would be to make two blurred tail objects that follow the ends of the path as it is animating.
Roundabout solution, sorry I can't think of another versatile way!
How can I achieve a stretch animation look?
Where do I need to start looking? CABasicAnimation does not seem to do the trick. Something like this: is the desired effect:
http://inspirationmobile.tumblr.com/post/112168531484/sidebar-animation-by-jacub-antalik-ramotion-com
There is a lot more than a simple animation in this view.
It is used UIKitDynamics, but the smart thing is that the final effect is composed by the single effects of small invisible UIViews inside that view.
The border is a CAShapeLayer made by a combination of bezier path that interpolates those single views.
Each drawing cicle, the CAShapeLayer path is refreshed based on the position of those views.
You can find more about this effect here.
For such a complicated shape animation look into CAShapeLayer and UIBezierPath. Look at this SO thread that might help you to get started at least.
I'm trying to draw a graphic equaliser for an iOS project.
The equaliser will have 7 bars, representing different frequency bands, than move up and down based on real-time audio data.
Can anyone suggest the best way to approach this in iOS?
New frequency data comes in at about 11Hz, and so the bars would have to animate to a new size 11 times per second.
Do I create a UIView for each bar and dynamically resize it's frame height?
Do I draw the bars as thick CGStrokes and redraw them within the parent view as needed?
Another option?
Thanks in advance
You want to use Core Animation. The basic principle is to create a bunch of "layer" objects, which can either be bitmap images, vector shapes, or text. Each layer is stored on the GPU and most operations can be animated at 60 frames per second.
Think of layers like a DOM node in a HTML page, they can be nested inside each other and you can apply attributes to each one similar to CSS. The list of attributes available matches everything the GPU can do efficiently.
It sounds like you want vector shapes. Basically you create all your shapes at startup, for example in the awakeFromNib method of a UIView subclass. For simple rectangles use CALayer and set a background colour. For more complicated shapes create a CAShapeLayer and a UIBezierPath, then apply it with shapeLayer.path = bezierPath.CGPath;.
Then, whenever you want to change something, you apply those changes to the layer object. For example, here I'm rotating a layer with a 1 second linear animation:
[CATransaction begin];
[CATransaction setAnimationDuration:1];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
[self.needleLayer setValue:[NSNumber numberWithFloat:DegreesToRadians(degrees) forKeyPath:#"transform.rotation.z"];
[CATransaction commit];
// you'll want to declare this somewhere
CGFloat DegreesToRadians(CGFloat degrees)
{
return degrees * M_PI / 180;
}
More complicated animations, eg a series of changes scheduled to execute back to back, can be done using a CAKeyframeAnimation: https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/CoreAnimation_guide/CreatingBasicAnimations/CreatingBasicAnimations.html
Note Core Animation only does 2D graphics. Apple has Scene Kit which is basically the same thing for 3D, but so far it's only available on OS X. Hopefully iOS 8 will include it, but until then if you want 3D graphics on iOS you need to use Open GL.
CALayers which you resize on demand would probably be the most efficient way to do this, if the bars are solid colours. This allows you to optionally animate between sizes as well.
View resizing triggers off layout cycles, which you don't want. Drawing using CG calls is pretty slow.
Obviously the only real way to find out is to profile (on a device) using instruments and the core animation tool. But from experience, layer sizing is faster than drawing.
Definitely not a UIView for each - instead, a single UIView for the entire equalizer. Fill in the drawRect method with the appropriate CG calls to draw whatever is required. You can queue the view to refresh as needed with the appropriate data. Tapping into CADisplayLink will help you get the frame-rate you're looking for.
https://developer.apple.com/library/ios/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html
NOTE: You can also subclass CALayer and draw in it if you prefer something lighter-weight than UIView but I think you'll be fine with the former.
I would like to draw a graph with CAShapeLayer and set a pretty shadow on a background for some nice visual effect. With shadows, animation of my graph (straight line to graph modeled using some data) really slows down!. I read about shadowPath property and decided to use it, but the problem is, despite the fact wether I close the CGMutable path or not, it seems like CoreAnimation closes it, and this shadow I get, instead of a line, is rather a polygon. Looks really ugly.
Help!