I need to detect when a CATiledLayer has finished drawing. I tried subclassing and overriding -(void)display to set/clear a flag, but it seems that tile drawing is happening in a different thread (display just returns and then seconds later, the layer is finished drawing)
Some things are not clear from your question. Are you asking whether all tiles have been drawn or just whether the visible tiles have finished drawing?
Assuming the later you could try drawInContext: but will likely still not give you the answer if there is scrolling. Because the tiles are cached and we have no way to know when the cached tiles are dumped you would not be able to tell if a tile has not been drawn yet or if it was just drawn from the cache.
You might want to describe what you are trying to accomplish and see if people have ideas for another way to do it.
Related
I am calling setNeedsDisplay from touches moved (and have also tried not calling from touches moved, but instead from a 0.05 timer) and the drawrect method is always laggy. Is their anyway to change this? I am doing a lot of drawing in drawrect but I have no idea for a solution to fix the lag. Even when the timer was called at a 1.0 interval than it still lagged when the timer called the selector. Also, I have no leaks (I checked using Xcode analyze feature ). Please help!!
EDIT: I am calling setNeedsDisplay, not drawRect from my timer/method
EDIT: It seems that wherever core graphics does somethings with a lot of drawing it always lags. I am positive I have no memory leaks and I even created another painting app and it lags (what is the fix to this?? Please help mee)
Slightly edited transcript of comments on one of the other answers:
I am drawing a color a hue based color picker (in draw rect a line for each hue value is drawn)
… Are you drawing 360 rectangles?
Yes, I am ….
I draw the images of 360 rectangles of different colors into the image of one UIImageView. than I release the rectangles. (I use a for loop for the rectangle allocation/releasing)
So, you are doing this 360 times:
Create an image.
Draw a rectangle into this image. (Or not, if step 1 loads the image from a file.)
Draw that image into another image.
Release the image.
And then you pass the image that you drew all the smaller images into to a UIImageView for the actual display?
It sounds like you're trying to cache this in the image, which should help after the first time if you do it right, but this doesn't need to be slow in the first place.
You already have a custom view. Drop the image view, and cut out all this rectangle-drawing (or image-drawing) code. If you have image files with the individual colored rectangles, delete them.
In your view's drawRect:, create a gradient and draw that. Your drawRect: will be three lines long and should be much, much, much faster.
I am calling drawrect from touches moved
don't do that.
(and have also tried not calling from touches moved, but instead from a 0.05 timer)
don't do that.
and the drawrect method is always laggy. Is their anyway to change this? I am doing a lot of drawing in drawrect but I have no idea for a solution to fix the lag. Even when the timer was called at a 1.0 interval than it still lagged when the timer called the selector. Also, I have no leaks (I checked using Xcode analyze feature ). Please help!!
Yes!
You should NEVER call drawRect explicitly. Use setNeedsDisplay instead and the drawing will be performed when the system is ready for it.
EDIT:
Based on the fact that you were already doing this. Your problem is then your drawRect is just too slow. What are you trying to draw?
If you can figure out which parts of the screen needs change, you can call setNeedsDisplayInRect to speed it up by just redrawing the changed rect instead of the whole screen.
You can also run a background thread to prepare frames in a buffer and use that to draw on screen. It depends on the kind of drawing you are doing. Here is a blog post I found on this topic.
Or you can use OpenGL ES to draw.
This issue has been encountered by someone else, but I've not seen any solutions - see ( http://openradar.appspot.com/6941930 ).
I have a UIScrollView whose content view's layer contains two CATiledLayers. One tiled layer draws content quite fast (but long enough to make drawInRect: too slow!), the other draws them slowly.
The quicker CATiledLayer doesn't Zoom has has default LOD settings, with a large tile size.
The slower CATiledLayer is zoomable.
Using just one of the CATiledLayers behaves as expected.
The problem is adding both layers to the visible screen causes one or the other to behave slowly. Annoyingly, it seems to make the content that draws normally fast, much slower.
I've not seen a way yet to reliably know when one CATiledLayer has completed its draw.
I think the solution would be to queue the Tiled Layers, so draw the slower tiled layer when the quicker one has finished. But the documentation on CATiledLayer is pretty bad.
I've tried to use a NSTimer, and set it for a 3 second wait before drawing the 2nd Tiled Layer. This however does not seem to solve the problem! I'm lost!
CATiledLayer seems to favor any tiles near the center of the screen. Perhaps one of your layer's is closer to an edge?
I am working on an application where I render PDF content in a CATiledLayer. I want to trigger one method after the rendering of the tiled layer is complete.
Is there any delegate method that will be called immediately after the rendering of all visible tiles is completed? Is there any other way of knowing when this is finished?
You can calculate the number of tiles your drawing requires before it's drawn. In drawRect of the tilingview, each tile is drawn only ONCE. So put a counter in part of the draw rect that calls a new tile. When your counter reaches the total number, call your method.
Keep in mind that drawrect for tiling is done on a background thread.
This requires some creative thinking. I've had a similar problem where I've needed to abort the rendering of a tiled layer mid-cycle. The way I worked round it is somewhat complex, but seems to work reasonably well. It involves wrapping the draw calls to the tiled layer inside a NSThread. Threads have a isFinished bool that you can key-value observe to discover when a tiled layer has completed its render.
If you're not comfortable with threading on iOS this may be more trouble than its worth, but will give you the advantage of knowing when the rendering has finished, and also being able to cancel the thread operation (and thus the render) if required.
I'm looking into the Apple ScrollViewSuite and the Photoscroller, and I wonder how to implement a CATiledLayer when downloading the tiles through an NSURLConnection:
how do I notify drawRect: that a specific tile has been downloaded and how do I keep track of the rects and contexts associated with each tile?
Regards Fredrik
When you finish downloading, cache the results, and then call setNeedsDisplayInRect: with the tile's rect. It will call drawLayer:inContext: again, and you can then draw the cached results.
I thing there are just no way for doing this because it is supposed to work to other way around. Tiles are rendered in separated thread and thus you could start downloading the tile from the drawLayer:InContext: method. Of course don't forget to implement caching for the downloaded tiles otherwise you will kill both your app and your invoice :-)
There's no way to get the necessary information out of the tiled layer. I'm currently simply invalidating/redrawing the layer once every few seconds to get some kind of "eventual consistency". That is, at some point in time I expect all visible tiles to have been loaded and cached (by my own controller). The redraw will then simply render all tiles using images from my cache.
For that to work, you need to be able to calculate the set of visible tiles and (re)download them if they're not cached. This approach has the huge benefit of allowing me to cancel connections for tiles that are no longer visible. And it allows the map to, eventually, recover from connection/server errors etcetera.
The tiled layer would need to export a lot of internals if you wanted to get away from this 'polling' approach, like which tiles it has cached and which it is currently interested in.
Currently, I have a UIView subclass that "stamps" a single 2px by 2px CGLayerRef across the screen, up to 160 x 240 times.
I currently animate this by moving the UIView "up" the screen 2 pixels (actually, a UIImageView) and then drawing the next "row".
Would using multiple CALayer layers speed up performance of rendering this animation?
Are there tutorials, sample applications or code snippets for use of CALayer with the iPhone SDK?
The reason I ask is that most of the code snippets I find that demonstrate simple examples of CALayer employ method calls that do not work with the iPhone SDK. I appreciate any advice or pointers.
Okay, well, if you want something that has some good examples of CA good that draws things like that and works on the phone, I recommend the GeekGameBoard code that Jens Aflke published (it is an improved version of some Apple demo code).
Based on what you are describing I think you are doing somthing way more complicated than it needs be. My impression is you want basically a static view that you are animating by shifting its position so that it is partially off screen. If you just need to set some static content in your drawRect going through layers is not going to be faster than just calling CGFillRect() with your color. After that you could just use implicit animations and the animator proxy on UIView to move the view. I suspect you could even get rid of the custom drawRect: implementation with a patterned UIColor, but I honestly have not benchmarked the difference between the two.
What CALayer methods are you seeing that don't work on iPhone? Aside from animation features tied to CoreImage I have not noticed much that is missing. The big thing you are likely to notice is that all views are layer backed (so you do not need to do anything special to use layers, you can just grab a UIView's layer through the layer accessors methos), and the coordinate system has a top left origin.
In any event, generally having more things is slower than having fewer things. If you are just repeating the same pattern over and over again you are likely to find the best performance is implementing a custom UIView/CALayer/UIColor that knows how to draw what you want, rather than placing visually identical layers or views next to each other.
Having said that, generally layers are lighter weight than views, so if you have a lot of separate elements that you need to keep logically separated you will find that moving to layers can be a win over using views.
You might want to look at -[UIColor initWithPatternImage:] depending on exactly what you are trying to do. If you are using this two pixel pattern as a background color you could just make a UIColor that draws it and set the background.
What CALayer methods are you seeing that don't work on iPhone?
As one example, I tried implementing the grid demo here, without much luck. It looks like CAConstraintLayoutManager and CAConstraint are not available in QuartzCore.h.
In another attempt, I tried a very simple, small 20x20 CALayer object as a sublayer of my UIView's layer property, but that didn't show up.
Right now, I have a custom UIView of which I override the drawRect method. In drawRect I grab a context and render two types of CGLayerRefs:
At "off" cells I draw the background color across the entire 320x480 canvas.
At "on" cells, I either draw a single CGLayerRef across a grid of 320x480 pixels (initialization) or across a 320x2 row (animation).
During animation, I make a UIImageView clip view from 320x478 pixels, and draw a single row. This "pushes" my bitmap up the screen two pixels at a time.
Basically, I'd like to test whether or not using CALayer will accomplish two things:
Make my rendering faster, if CALayer has less overhead than what I'm doing now
Make my animation smoother, by letting me transition a layer up the screen smoothly
Unfortunately, I can't seem to get a basic CALayer working at the moment, and haven't found a good chunk of sample code to look at and play with.