I've got a UIScrollView with a (custom) UIView inside of it.
In my scrollViewDidScroll-method I'm calling
[myCustomView setNeedsDisplay];
This makes the scrolling noticeably slower, if I'm implementing the drawRect: method in my custom UIView - even if it's completely empty.
As soon as I delete the drawRect: method, it's smooth again.
I have absolutely no idea, why... anyone of you?
I hate drawrect too
"It's because of the way hardware-accellerated animation works in Cocoa.
If you don't have a custom drawRect method, the system caches the pixels for your view in video memory. When it needs to redraw them, it just blits the pixels onto the screen.
If you have a custom drawRect method, the system instead has to call your drawRect method to render the pixels in main memory, then copy those pixels into video memory, THEN draw the pixels to the screen for each an every frame. The docs say to avoid drawRect if you can.
I think main memory and video memory are shared on most/all iOS devices, but the system still has a lot more work to do when you implement a drawRect method for a view.
You would probably be better served to use an OpenGL layer and render to that with OpenGL calls, since OpenGL talks directly with the display hardware."
link to that quote: http://www.iphonedevsdk.com/forum/iphone-sdk-development/80637-drawrect-makes-scrolling-slow-uiscrollview.html
Related
I have a custom NSView that has it's own drawRect. I draw a lot of images using CGContextDrawImage into the view. The NsView is in a NSScrollView. The issue I am having is, while scrolling, the drawrect takes too long. the scrolling isn't smooth.
I did try setting wantslayer to true, to enable layer-backed drawing. Now the scrolling is smooth until the next drawrect is called, i.e till next time a new dirtyRect has to be redrawn.
Since I have to use drawrect to draw the contents, is there a way to improve scrolling performance?
if not, is there an alternative?
Update:
the call that's taking time.
I tried what Rob suggested below (using NSImageView for each image). that helped. I see smooth scrolling while setting the images as well.
But I would like to know why CGContextDrawImage takes time? especially rgbaf16_sample_rgbaf_inner. is there a way to avoid it?
I have a sub-classed CAOpenGLLayer class which overrides drawInCGLContext there I draw a rectangle with OpenGL. The CAOpenGLLayer is added to a CALayer and shown.
So when I would like to draw something I would need to do it in drawInCGLContext with this architecture.
What I would like to have is a sort of context used by an other class to draw, animate or render to but will be displayed every time drawInCGLContext occurs.
So basically the only thing my subclass should do is display a remote (OpenGL)context, what's the best way to achieve this? Or should I consider a different approach?
*Not using a CALayer is not an option.
Have you considered using a frame buffer object (FBO)? You can create one which is backed by a texture. Your "remote" drawing class could draw into the FBO, which will cause the drawing to go to the texture that backs it. You can then use that texture elsewhere, like blitting it to the screen in your CAOpenGLLayer subclass. See this link for details of how to use an FBO.
I needed to display some Cocoa widgets on top of an NSOpenGLView in an existing app. I followed the example in Apple's LayerBackedOpenGLView example code. The NSOpenGLView is given a backing layer using:
[glView setWantsLayer:YES]
Then the Cocoa NSView with the widgets is added as a subview of the glView. This is basically working and is twice ad fast as my previous approach where I added the NSView containing the widgets to a child window of the window containing the glView (this was the other solution I found on the web).
There were two problems.
The first is that some textures that I use with blending were no longer getting the blend right. After searching around a bit it looked like I might need to clear the alpha channel of the OpenGLView. This bit of code that I call after drawing a frame seems to have fixed this problem:
Code:
glColorMask(FALSE, FALSE, FALSE, TRUE); //This ensures that only alpha will be effected
glClearColor(0, 0, 0, 1); //alphaValue - Value to which you need to clear
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(TRUE, TRUE, TRUE, TRUE); //Put color mask back to what it was.
Can someone explain why this is needed when using the CALayer, but not without?
The second problem I don't have a solution for. It seems that when I pan to the part of the scene where problem is #1 was observed, the frame rate drops from something like 110 FPS down to 10 FPS. Again, this only started happening after I added the backing layer. This doesn't always happen. Sometimes the FPS stays high when panning over this part of the scene but that is rare. I assume it must have something with how the textures here are blended, but I have no idea what.
Any thoughts?
I did figure out a workaround to the slowdown. The OpenGL view has a HUD (heads up display) view that goes on top of it. I had installed another NSView as a subview if it. Both the HUD and the subview have lots of alpha manipulation and for some reason that tickled a real slowdown in compositing the layers. I could easily instal this subview as a subview of the OpenGL view and when I did this everything sped up again. So although I don't fully understand the slowdown, I do have a good work around for it.
I've got this giant UIView with around 600 UIButtons as subviews. The UIView is about 2000 by 2000 pixels and it's a subview on itself for a UIScrollView. I'm trying to implement CATiledLayer as the rendering layer for this giant UIView but I can't seem to figure out how to render the tiles. I found a lot of examples that cover CATiledLayer with tiled images, pdf's, ... but I never found a real example on how to draw a complete UIView with a lot of subviews. You're probably asking why i'd like to stick with UIView? Because I'd like the users to keep on using the buttons to interact with the view.
I'm wondering if someone has an example or some psuedo code on how to implement the - (void)drawLayer:(CALayer*)layer inContext:(CGContextRef)context method, keeping in mind that there's 1 giant UIView with a lot of UIbuttons as its subviews.
Starting with iOS 4.0, you can use drawRect: instead of drawLayer:inContext:. This Q&A from Apple explains it: http://developer.apple.com/library/ios/#qa/qa1637/_index.html. The important point from it is that drawRect: in 4 is now thread-safe and you can use UIKit graphics functionality.
You still need to override this method to use a CATileLayer:
+(Class)layerClass
{
return [CATiledLayer class];
}
But you can now just use drawRect:
-(void)drawRect:(CGRect)rect
{
//normal drawing routines here.
}
The rect that is delivered to the method will be the size of your tiles and you need to determine what needs to be drawn in the particular rect. The power of CATiledLayer is that it only calls for drawing the tiles that are showing on the screen. It also uses background threads and is very fast.
I have not used CATiledLayer with UIViews, only large images. Are the UIViews subclasses with their own drawRect: implementations? I think you will need to determine which views will be showing in the current rect and call their drawRect: method.
Using drawRect: is only for iOS 4.0 and later, it won't work for older versions.
In core animation or in App kit When we say layer-backed view or simply add a layer in the view,then actually what we mean by the layer.
A simple Google search:
The CALayer is the canvas upon which everything in Core Animation is painted. When you define movement, color changes, image effects, and so on, those are applied to CALayer objects. From a code perspective, CALayers are a lightweight representation similar to an NSView. In fact, NSView objects can be manipulated via their CALayer. This is referred to as being layer-backed.
A CALayer is an object which manages and draws upon a GL surface, and can manipulate that surface's location in three dimensions, without needing to redraw its contents.