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:.
Related
Well, I want to make a animation application on iOS. So, I make an application using UIViews... But, it's very complicated. There are so many UIViews so I want to make it simple. So, There's any way to make an animation just using CGContext just one UIView? If someone knows PLS help.
You could use a timer that repeatedly fires so you call setNeedsDisplay every 0.01 second or something, and calculate every rectangle, circle, line, whatever to the right places at every time instant. You may well suffer some issues this way, though, since drawRect won't get called instantly every time, meaning you'll need to check the actual time when drawRect fires. You might even need to take the time it takes to execute your code into account.
Or just move the UIViews around in stead, using animateWithDuration:. I recommend that. No matter how complex your hierarchy is, doing it all in a single view will always be more complex.
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.
I've a got a loop running at a separate thread updating the location and rotation of stuff, it runs at more than enough FPS and calls setNeedsDisplay on the main thread in the end. The problem is that the framework doesn't call drawRect enough times, on average i get 10 drawRect (FPS) calls per sec. I tried calling CATransaction flush but it seems to have no effect at all.
The drawing i'm doing is CGContextShowTextAtPoint. That's pretty much it, after rotating the context around and such. Is there any way for me to make it draw at a higher frequency? I wouldn't want to use openGL or cocos2d because the drawing code work pretty good.
I would suggest setting up an NSTimer on the main thread to call drawRect every 1.0/30 or 1.0/60 seconds.
Maybe the code running on the separate thread is working the overall system too hard. Therefore the main thread doesn't get enough compute resources to update the view often enough. The "more than enough FPS" may be just wasted calculations if the extra frames of data are never shown. Can the separate thread pause after updating the location and rotation and wait for the main thread to update the view before calculating the next location and rotation? The NSTimer can send a message to the second thread to calculate the next frame's data.
Calling setNeedsDisplay does not cause the view to be redrawn immediately, it just marks the view as dirty which will cause it to be redrawn at the next drawing cycle.
I think it will be difficult to achieve fast and smooth animations using the setNeedsDisplay method.
Have you tried using core animation instead?
One final note: if you do use setNeedsDisplay make sure you always call it from the main thread.
I ended up using Cocos2d for animation the rotations and such. It seems that using Core Graphics to manually move stuff at those FPS rates is too painful and would require hackish approaches.
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 using a NSTimer to fire a drawRect within the app's main view. The drawRect draws a few images, and blends each using kCGBlendModeScreen (which is an absolute must). However, the drawRect takes just a tad longer than desired, so the timer doesn't always get fired at the desired rate (more like half as often).
I've optimized the graphics used as much as I feel is possible, so I'm wondering if it's possible to "outsource" the drawing by creating a new view, and calling that view's drawRect from within a thread created inside of the timer's callback method. (In other words, thread the call to a new view's drawRect, such as [someNewView setNeedsDisplay] ...)
If so, how might I approach something like that, in code?
...
I'd use Core Animation, but I remember reading that it didn't support alpha blend modes. If I'm wrong, I'd be open to seeing some example code that allows animation of all the images in separate transformations (e.g. individual rotations for each image), while still keeping them able to blend using kCGBlendModeScreen that I'm currently implementing.
Thanks for any tips!
The answer is "no." You should never, ever do drawing(or anything with UIKit) from a secondary thread. If you're experiencing performance issues, you should perform all of your computations on another thread ahead of drawing so that drawing takes a minimal amount of time.