Laggy scrolling in UITextView while a CABasicAnimation is running - iphone

I've implemented a simple animation in my app. I achieve the animation by cross-fading between four image frames using a CABasicAnimation that has a from and to value set to (id)(myimage.CGImage). The animation is on its own CALayer but while scrolling a pop-up UITextView in its own small subview, the text scrolling is very jerky during a frame blend, and also pauses the animation while scrolling. Would putting the animation in its own thread alleviate the problem or is this simply to do with a complex operation being done by Core Animation?
Thanks for any help!

You can send UI animations to a separate thread but any actual updates to the application's UI are always handled/drawn by the main thread. So in that sense I think adding additional threads is not going to help, particularly if you have tried that already and it still does not give acceptable results.
The only thing I can think of that might help would be to use OpenGL to render your animation instead of Core Animation. That will pull a complete layer of abstraction out of the picture, and should speed things up quite a bit as a result. Of course, it will also likely take a lot more code to accomplish the same thing, so there is certainly a trade-off to be made.

Related

Continuous drawing with Quartz? frame by frame possible?

I've been working on custom drawings using drawRect in UIView subclasses. That's cool, but you have to wait until the end of the run loop for drawRect to be called and I'm wondering how you can control frame by frame animations where you change the drawings over time, or if this is possible? Perhaps Quartz isn't really designed for this type of animated graphics? I gather that perhaps it is designed for static drawings that don't change so frequently.
Quartz by itself its not able to sustain a high frame rate, due to its need to redraw everything each time. But you can have Quartz work together with CoreAnimation to have Quartz based animations. The idea behind this is that you can cache previously drawn content inside CALayer objects and then use CoreAnimation to create the continuous drawing effect.
A good example of this technique can be see in the AccelerometerGraph sample code provided by Apple. Inside this sample the UIView subclass that uses this technique is the "GraphView" object. Basically this object draws as completely new only a portion of the graph (the newly generated segments), backs it in a dedicated layer and then animates the layers in order to provide the "scrolling graph" animation.
Clearly this technique works only when you have full control of the drawing elements and can manage this incremental way of adding objects in the screen. Of course things become much more complicated when you must redraw many different parts of the screen and you need to modify previously generated layers.
Anyway have a look at the mentioned code: it is quite interesting.
Your app should exit to the run loop before each frame. Do all your custom frame animation setup between each frame. So frame-by-frame drawing in drawRect should work just fine. This can work in iOS apps at a 60 Hz frame update rate, not just for static views, as long as all your methods between frame times, as well as your drawRects, are fast enough. Chop them up if needed.

How do I make animations in an iPad app?

I'm developing an application for iPad.
I want to make nice animations so that I can beautify my application.
For example, there are 4 main buttons/images in a view.
When tapping on one of them, a few more buttons/images will branch out.
It's like the 'parent button' will branch out to few 'child buttons'.
How are these kind of animations done?
Are there any good references or code snippets to refer to?
Thanks.
A good stating point would be the Core Animation demo's here:
https://github.com/neror/CA360
Run them in the iOS Simulator and checkout the code that creates the magic.
UIView animations would also be suitable for your example, and are a little easier to implement. There is a nice tutorial here:
http://www.raywenderlich.com/2454/how-to-use-uiview-animation-tutorial
Search on the term "Core Animation iOS" on your favorite search engine. You'll find information from Apple's Developer Central site, particularly the Core Animation Guide and Cookbook.
There are actually 2 main methods.
One is to use Core Animation, if it contains your desired path and animation.
The other is to use an animation game loop, where the app periodically calls a routine to redraw the view every frame time. An NSTimer or CADisplayLink can periodically (say at 24 or 30 or 60 Hz) call a routine doing a setNeedsDisplay, which then causes the view's drawRect to be called, etc. The some other periodic code can change some state (moving some X Y button positions, etc.) during or between each frame to provide the appearance of movement or other animation effect, when the view is redrawn. Or OpenGL can be used to redraw some animated 3D world as things move. You can even have each frame change in response to user input. This is the most flexible way to animate, and allows you to customize animations in ways that are impossible for Core Animation, but it uses more power and can be so CPU intensive as to be a lot slower than Core Animation as well.

Subclassing an UIView efficiently

Guys I'm having some troubles subclassing an UIView.
I'm creating an IconView.
Simply it's a container for some other subviews.
In my IconView i have this iVar:
UIImageView _background
UIImageView _icon
UILabel _iconLabel.
When I initialize the IconView I setup this 3 iVar with images, text and some quartz effect like roundCorner and Shadow and then I add them to the self view.
Everything is Ok but if I insert some of this IconView (i.e. 10) inside an empty scrollview the scroll effect is not smooth. I tried before inserting thousand of simple UIViews in a scrollview and the scroll animation works perfectly.
With just 10 of my IconView the scroll animation works really bad.
I could approach differently retaining UIImages instead of UIImageViews and draw it inside drawRect: method but in this case I'm gonna loose Autoresizing property and Quartz effect.
Any suggests?
Thank, Gabriele.
Unfortunately, a UIScrollView gets slow pretty fast. There are a lots of posts and articles on this topic out there, like this Question and this (defect) blogpost along with it's sample code. There are also three sessions about 'Performance optimization in iOS' in the 2010's WWDC videos which I highly recommend to watch. To summarize the conclusions: Use as few subviews as you can and take special care of avoiding transparencies.
Ok, so much for the general 'Performance in ScrollViews' talk, now to your case: Having the same problem, I used all the tips from the articles and videos above and while they improved the performance, it just wasn't enough. I had, like you, used rounded corners one some images and I found out that this absolutely kills performance. Just deactivating them helped more than everything else. It's probably the same with the shadow effects.
Now, most likely, you want to keep those rounded corners. I would suggest that you create a copy of your images (or take the original, if possible) and than manipulate them directly, using those awesome classes. This way, the effects will only be applied once. It works perfectly for me. For you shadows, you can probably just create some in Photoshop and use them in a new ImageView.
If that isn't enough, you should try to cache your IconViews, like TableViewCells are cached, if you don't already do this.
The problem will probably be the Quartz shadows. They can really slow the rendering down if used a lot.
Before you write them off, you might try setting your CALayer's shouldRasterize property to YES. This makes quartz render the shadow only once and store it in a buffer. See how it goes.

Elements/Wired Style 3D Animation

I am trying to implement the style of 3D rotational animation you see in the Elements and the Wired iPad applications. The animation has the UIScrollView style acceleration so it looks like they have connected a UIScrollView to a sequence of images.
I have tried implementing it in UIKit, using an "empty" UIScrollView to increment through an array of images and set the contents of a UIImageView, based on the content offset. This works but even with scaled down images on the simulator, it is very sluggish. I am not sure how I could optimise it to make it run faster.
Source Code on GitHub
I have also tried doing something similar in Cocos2D. Cocos could animate the sequence very smoothly but I couldn't control the animation. I tried using a scroll view and setDisplayFrame to step through an animation but it didn't work.
Any help or suggestions on either option would be greatly appreciated.
I'm not 100% sure I understand the effect you're trying to achieve, but if you're referring to the images in "The Elements" app which you can spin around, then yeah, you probably don't want to be doing that with an UIScrollView.
You can control the animation of content in Cocos2d. While you can set an animation to "just run", you can also manually set frames directly out of an animation, so if you can get smooth animation out of Cocos, you should be able to do it interactively. Look at the atlas sprite object - you can set the rect of an animation frame directly.
However, again, I'm not sure this is the best approach, either, since that's a lot of frames for a texture. If you're going to be doing a lot of 3D photographic objects, you might consider looking into the video codecs and scrubbing along a video instead of trying to pack all the frames of a rotation animation into a series of textures.
Just so you know, the guy who made "The Elements" wrote an article about the development of the app, and here's what he had to say about the 3D rotatable objects:
"Creating fluidly spinning objects with the level of crystal clear photographic quality I demanded is actually harder than it might seem. A number of the obvious things the more technical of you might think of turn out not to work for reasons too complicated to get into here. Suffice to say that John designed a brilliant solution that uses the iPad's excellent graphics subsystem to lend a hand in ways it's not normally intended to."
I don't know how exactly they achieved the 3D objects, but perhaps that "uses the iPad's excellent graphics subsystem" mention gives you a clue where to head from here...

can't draw fast enough to keep up with touchesMoved?

I am trying to implement simple paint functionality in my iPhone app. I tried updating a bitmap with a bitmap brush, and I also tried this tutorial.
Both methods have the same problem, even though the code is almost totally different. It happens only on the device - the simulator works fine.
When I touch the screen and move my finger, the screen does not get updated. When I pause or lift my finger, then the screen gets updated. This is not a very good user experience!
I tried calling drawRect from touchesMoved directly, but found that the drawing context (which I retrieve using UIGraphicsGetCurrentContext) is invalid for many of the calls, so painting the screen myself for every touchesMoved doesn't work.
Any ideas?
Thanks for any help, this has been quite frustrating!
Henning
It sounds to me like you're not giving the main run loop a chance to update the display. Your drawing code may be taking longer to execute than the time between touch events, so the display is never updated. When you lift your finger, it does the updating because it's no longer burdened with your drawing.
You might consider optimizing your drawing to speed it up (drawing only within the dirty region of the screen, for example), using something like NSOperationQueue to queue up the heavy calculations of your drawing to run on a background thread, or selectively dropping touch drawing events to keep your response smooth.
One additional possibility is placing your heavy drawing code in a separate method and calling it via performSelector:withObject:afterDelay, with a 10 millisecond (or smaller) delay. This might give the main run loop a chance to update the display with its current state. I haven't tested this, but if I remember correctly I've seen this work.
You can't directly call drawRect:. To refresh your screen on demand, try calling [self setNeedsDisplay] from your touchesMoved method, which will setup the proper contexts for a call to drawRect:.