I have several UIButtons on a UIScrollView. I want the buttons to have rounded corners, so I call maskToBounds: on each of them. When I do this and run on the device, the scrolling framerate is pretty bad (it works fine on the simulator). Any ideas on a workaround for this problem?
You're causing the view to be composited offscreen with that call to masksToBounds:, which slows things down quite a bit. Are you rendering custom button images? If so use UIImage -stretchableImageWithLeftCapWidth:topCapHeight: with an image which is the minimum width to encompass it's rounded edges. This allows the GPU to handle stretching the image in the most efficient way possible, while still giving you a button made out of an image. There is a session in the WWDC 2011 videos on Drawing in UIKit - watch that, as it addresses exactly this problem, and a few others you're likely to have.
A few alternative methods:
Tweeties implementation of fast scrolling, by drawing everything manually
Matt Gallaghers implementation of custom drawing. This is the method I use, as it's easy to maintain
Related
I have been working on an iPad app that performs animations on very large images (full screen images that can be zoomed at 2x and still be retina quality). I have spent a lot of time getting smooth transitions when zooming and panning. When running the app on iOS7 however, the animations become really jerky (slow frame rate).
Further testing shows that it is the zoom animation that causes the problem (panning does not cause a problem). Interestingly, I have been able to fix it by setting the alpha of the image being scaled to 0.995 (instead of 1.0).
I have two questions
What has changed in iOS7 to make this happen?
Why does changing the opacity of the view make a difference?
Further information for the above questions:
Animation Setup
The animations are all pre-defined and are played upon user interaction. The animations are all a mix of pan and zoom. The animations are really simple:
[UIView animateWithDuration:animationDuration delay:animationDelay options:UIViewAnimationOptionCurveEaseInOut animations:^{
self.frame = nextFrame;
//...
} completion:^(BOOL finished) {
//...
}];
To fix the jerky animation, I set the alpha before the animation
self.alpha = 0.99;
Some interesting points:
Setting the alpha inside of the animation works as well
Setting the alpha back to 1.0 after the animation and then doing the reverse animation with a 1.0 alpha does not give a smooth reverse animation.
Opacity fix
I have previously used the opacity fix to make animations smooth when scaling and panning multiple images. For example, I had two large images panning and scaling at different speeds with one on top of the other. When a previously un-rendered part of the lower image (the image on the bottom) became visible, the animation would become jerky (panning as well as scaling). My thought for why alpha helps in this case is, if the top image has a bit of transparency, the bottom image must always be rendered, which means it can be cached before the animation takes place. This thought is backed by doing the reverse animation and not seeing the jerky animation. (I guess I would be interested to know if anyone has different thoughts on this as well).
Having said the above, I don't know how this would have an affect when there is just one image (as in the situation I am describing in my question). Particularly when after getting the jerky animation, the reverse animation is still jerky. Another point of difference between the two situations is that it is only scaling that causes the problem in the current issue, while in the double image issue it was panning as well as scaling.
I hope the above is clear - any insights appreciated.
Look at Group Opacity. iOS 7 has that turned ON by default and this changes the way views/layers are composited:
When the UIViewGroupOpacity key is not present, the default value is
now YES. The default was previously NO.
This means that subviews of a transparent view will first be
composited onto that transparent view, then the precomposited subtree
will be drawn as a whole onto the background. A NO setting results in
less expensive, but also less accurate, compositing: each view in the
transparent subtree is composited onto what’s underneath it, according
to the parent’s opacity, in the normal painter’s algorithm order.
(source: iOS7 Release Notes)
With this setting on, compositing - also during animations - is way more expensive.
Also, have a look at the CoreGraphics Instruments tool to check if you have lots of off-screen images compositing going on.
Are you having any sort of changes going on in the view being animated? That would trigger more discarding of the rendered layer image from the backing store.
I am using CADisplayLink to draw frames using the EAGLView method in a game at 60 times per second.
When I call UIView animateWithDuration: the framerate drops down to exactly half, from 60 to 30 fps for the duration of the animation. Once the animation is over, the fps rises instantly back up to 60.
I also tried using NSTimer animation method instead of CADisplayLink and still get the same result.
The same behavior happens when I press the volume buttons while the speaker icon is fading out, so it may be using animateWithDuration. As I would like to be able to handle the speaker icon smoothly in my app, this means I can't just rewrite my animation code to use a different method other than animateWithDuration, but need to find a solution that works with it.
I am aware that there is an option to slow down animations for debug on the simulator, however, I am experiencing this on the device and no such option is enabled. I also tried using various options for animateWithDuration such as the linear one and the user interaction, but none had an improvement.
I am also aware I can design an engine that can still work with a frame rate that varies widely. However, this is not an ideal solution to this problem, as high fps is desirable for games.
Has someone seen this problem or solved it before?
The solution to this is to do your own animation and blit during the CADisplayLink callback.
1) for the volume issue, put a small volume icon in the corner, or show it if the user takes some predefined touch action, and give them touch controls. With that input you can use AVAudioPlayer to vary the volume, and just avoid the system control altogether. you might even be able to determine the user has pressed the volume buttons, and pop some note saying do it your way. This gets you away from any animations happening by the system.
2) When you have an animation you want to do, well, create a series of images in code (either then or before hand), and every so many callbacks in the displayLink blit the image to the screen.
Here's an old thread that describes similar drops in frame rate. In that case, the cause of the problem was adding two or more semi-transparent sprites, but I'd guess that any time you try to composite several layers together you may be doing enough work to cut the frame rate, and animateWithDuration very likely does exactly that kind of thing.
Either use OpenGL or CoreAnimation. They are not compatible.
To test this remove any UIView animation, the frame rate will be what you expect. Add back UIView animation, it will drop to 30fps.
You said:
When I call UIView animateWithDuration: the framerate drops down to exactly half, from 60 to 30 fps for the duration of the animation. Once the animation is over, the fps rises instantly back up to 60
I dont know why your not accepting my answer, this is exactly what happens when you combine UIView animation with CA animation not using a UIView.
I am trying to implement a Fan view like the ones in the Mac OS X dock, such as the Downloads and Documents folders, using Cocoa.
I am currently adding a button on a transparent window's content view and animating the button's frame using NSViewAnimation (group animation). But the animation is not as smooth as expected.
Is there any other optimized way for implementing this?
You should be using Core Animation for this. You should create a transparent view/window that's large enough to contain your whole animation. You should then use CALayer objects to perform the actual animation.
Core Animation layers are essentially high-level lightweight wrappers around OpenGL surfaces and the rendering of the layers is done by the GPU, giving much better performance than CPU-managed animation such as NSViewAnimation.
Bear in mind that because Core Animation layers are lightweight, they don't have any event-handling built into them, so you'll need to do all the mouse tracking in your view/view controller.
Your other option is using layer-backed views (which have their own CALayer) and animating the buttons' positions using the animator proxy. This may be enough to achieve what you want, and because the buttons are still full NSButton objects they still have all the NSView event handling.
You should probably read the Animation Overview to give you a better idea of how all these technologies work.
I'm making a game using UIView.
I use a large (8192x8192) UIView as the map area, (the game is birds-eye-view) with a UIImageView stretched across it displaying a grass texture.
This uses heaps of memory, doesn't run on older devices and nearly crashes Xcode whenever I try to edit it...
Is there an alternate method of creating a 8192x8192 map, but without being laggy?
If it's possible to tile your graphics, something involving CATiledLayer would probably be a good fit. CATiledLayer allows you to provide only the images that are necessary to display the currently viewable area of the view (just like Maps does).
Here is some example code for displaying a large PDF.
I'm trying to use a CAGradientLayer as a background for a UITableView. Everything works well until the overlaying view is scrolled, at which point then the pre-rendered background scrolls up and out of the way along with the original screen of data.
This is code that is being migrated from an iOS 3.1.3 app using a UIImage as a background to something device/resolution independent-looks great, works well, but sniffing the device type and using an alternate png isn't the sort of code that I want to ship, much less maintain.
Any suggestions as to how to do this?
Found the solution to what I was trying to solve, courtesy of Matt Gallagher:
http://cocoawithlove.com/2009/08/adding-shadow-effects-to-uitableview.html
His blog post has quite a few nice touches, including shadowing relevant cells instead of the whole table (mentioned as a performance issue in Noah's response).
You may have to make the table view transparent, and add the gradient layer to the table's superview. Keep in mind that your scrolling performance is probably going to be hideous—Core Animation will have to composite every subview of the table for every frame it displays. You may be able to slightly mitigate this by setting the cells' layers to rasterize themselves, as described here, but expect things to be pretty choppy regardless.