1

I use UIBezierPath for finger painting (my app). I create it using path = [UIBezierPath bezier path]; . It constantly lags on the iPad (and calling it from within drawrect did not change anything). I have been working on this for hours on end and have found no solution, just lag. Would somebody be so kind to help me please? Also, I am using a NSTimer to call the function. That is the old way my app will work so please help me fix this lagggg!!!!!

Aspyn
  • 647
  • 1
  • 10
  • 25
  • You need to post more details about your code for the question to be complete enough for anyone to help. For instance, are you calling setNeedsDisplay? How often? – hotpaw2 Oct 03 '11 at 01:45
  • YesI am, and I am calling it every 0.05 seconds (I have tried 1 second and whenever the timer hits that second it lags) – Aspyn Oct 03 '11 at 01:49
  • -1 because you have asked (basically) the same questions 4 times in the past day. seven people have gone out of their way to help you out and you ignore their advice and continue posting the same poorly formed question. – justin Oct 03 '11 at 01:54
  • I have rebuilt my project three times with the advice (I spent 7 hrs putting all my code in setNeedsDisplay. I have also tried threading and working with bitmap contexts. It has not worked for me.) I should not have posted the question again, I am sorry for that. The company I am doing work for has given me a deadline which is a few days from now, and I would like to get this app done by that dead line. I am sorry – Aspyn Oct 03 '11 at 01:57
  • ok. based on what i have read in this question/comments, there are a few possible areas for improvement (see below). it's hard for us to help (with accurate advice) when you don't detail your problems well. hotpaw2 and i both answered 'render to a bitmap' in your earlier questions -- you should have mentioned what you have tried to overcome the problem. – justin Oct 03 '11 at 02:14
  • @Justin: Rendering to a bitmap doesn't magically make slow code fast. It's a caching trick to be used only when there is no faster way. One of Aspyn's earlier questions provides an example: http://stackoverflow.com/questions/7627035/any-possible-way-for-coregraphics-to-not-lag-when-drawrect-is-being-called-from-t/7630563#7630563 – Peter Hosey Oct 03 '11 at 02:38
  • It would also go a long way if you'd give your questions meaningfully different titles. If your titles are all variations on the same thing, (1) it's likely to make people think they're the same question, and (2) if you can't think of a better title, they probably *are* all the same question. I think this is a materially different question, but it needs a better title. – Peter Hosey Oct 03 '11 at 02:42
  • @Peter Hosey i know it's not magic. in the commentary of another question in this series i wrote: *one recommendation i made involved precalculating results to a bitmap or image when you need to draw the same thing multiple times and the image takes a lot of time to calculate (profile to determine if it works for your needs, because it is a processor->memory tradeoff)*. nevertheless, i had not read the commentary in the post you linked. i'd assumed that the problem was with the drawing surface, not a color chooser. – justin Oct 03 '11 at 11:06

2 Answers2

10

Since none of your questions contains enough details on its own, I'm going to do something improper and post a meta-answer to your current last five questions.

First, draw in drawRect: and nowhere else.

That's so important that I'm going to say it again.

Draw in drawRect: and nowhere else.

Not touchesMoved:withEvent:.

Not touchesBegan: or Ended:.

drawRect: and nowhere else.

If you're making an image to save to a file, that's one thing. But when you're drawing to the screen, you don't do it anywhere other than drawRect:.

Seriously. It's that important.

Step 2: Don't attempt to force drawing to happen at any other time.

drawRect: is called for you when it's time for you to draw. By definition, at any other time, you don't need to draw, so drawing is doing things you don't need to do. By extension, don't call drawRect: yourself. You don't need to and it never helps.

Actually, that's just an extension of step 1. If you call drawRect:, it's no different from if you had the drawing code where you have the call. The drawing code isn't repeated everywhere, which is nice, but it's still running at the wrong time. Let drawRect: only be called when the system calls it.

setNeedsDisplay exists to tell the system that it's time to draw. You should do this when, and only when, something has changed that you'll need to draw. Your view's properties, something in the model—whenever what you will draw changes, send yourself setNeedsDisplay. Don't do it at any other time; you don't need to.

Cut out the timer. You don't need it. There's already a timer in place anyway, limiting you to 60 fps.

Core Graphics does not lag. No, really, it doesn't. Slowness or “lag” is because either you're trying to do too much or you're doing something wrong.

Don't cache unnecessarily. Nothing you're doing requires an image cache.

The “lag” is because you're trying to draw, or to force drawing, from touchesMoved:withEvent: and/or touchesBegan:/Ended:. See above.


Here's what you need to do:

In your touchesBegan:/Moved:/Ended: methods, or other responder methods as appropriate, update your state. This will include the Bézier path. Do not draw, and that includes do not call drawRect: or otherwise attempt to force drawing.

After you've updated your state, and only if you've done so, send yourself setNeedsDisplay.

In your drawRect: method, and only in your drawRect: method, draw the path, gradient, whatever, however you see fit.

Do these things, and your application will be fast. With Core Graphics. Without lag.


Also, some important reading:

Community
  • 1
  • 1
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
1

don't use a timer to periodically redraw when your view must be redrawn based on response to user events.

instead, invalidate your view using setNeedsDisplay or setNeedsDisplayInRect: when you receive touches which require you to redraw portions of your view.

a complex touch drawing iPad app can be a lot to ask to render in realtime, especially when you are overdrawing. draw only when needed.

another option: work in layers

you probably don't have undo in your app, so it should be simple to implement. periodically composite your image down to a single view. when draw rect is called, you then have fewer paths to draw (1 large image + fewer paths). in that case, a cached backing bitmap would definitely help.

again, there's not a whole lot of detail in your posts, and you haven't told us what the profiler is showing you.

justin
  • 104,054
  • 14
  • 179
  • 226
  • 2
    And, important point: *Look at what the profiler is showing you.* Don't just show it to us (although, yes, that is vital when you get to the point of asking on SO). Look at it yourself, really hard, and start by ironing out the hot spot(s) it shows you. – Peter Hosey Oct 03 '11 at 02:40
  • Thanks peter I will do that before I post a question like this again! – Aspyn Oct 03 '11 at 19:56