2

In my code I want to "animate" a delay of drawing a line, so after adding a new line to the view, I call setNeedsDisplay - which works fine once.

Inside the drawRect method I draw the line and call a method of the line to increment the line-lengthl. Now I want to call setNeedsDisplay again to redraw the line - so it get's an animation of "growing"..

But it only calls setNeedsDisplay once & never again, except I add another line. I also tried to call a method in this class, which calls setNeedsDisplay, to make sure you can't call it inside of drawRect..

- (void)drawRect:(CGRect)rect {

    for(GameLine *line in _lines) {

        if(line.done) {
            CGContextRef c = UIGraphicsGetCurrentContext();
            CGContextSetLineWidth(c, 5.0f);
            CGContextSetStrokeColor(c, lineColor);

            CGContextBeginPath(c);
            CGContextMoveToPoint(c, line.startPos.x, line.startPos.y);
            CGContextAddLineToPoint(c, line.endPos.x, line.endPos.y);
            CGContextStrokePath(c);
        }else {
            CGContextRef c = UIGraphicsGetCurrentContext();
            CGContextSetLineWidth(c, 5.0f);
            CGContextSetStrokeColor(c, delayColor);

            CGContextBeginPath(c);
            CGContextMoveToPoint(c, line.delayStartPos.x, line.delayStartPos.y);
            CGContextAddLineToPoint(c, line.delayEndPos.x, line.delayEndPos.y);
            CGContextStrokePath(c);

            [line incrementDelayLine];
            [self setNeedsDisplay];
        }
    }
}

_lines is a NSMutableArray with the GameLine objects (nonatomic, retain) property.

Christian 'fuzi' Orgler
  • 1,682
  • 8
  • 27
  • 51

2 Answers2

5

It is expected.

When you call setNeedsDisplay, you mark the view as needing to be redrawn. OK. The system gets it.
And it will be done the next time the main loop of your app runs.

If you really want to refresh the view now call:

[[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate: [NSDate date]];

just after setNeedsDisplay.

Indeed, apple documentation states (emphasis mine):

When the actual content of your view changes, it is your responsibility to notify the system that your view needs to be redrawn. You do this by calling your view’s setNeedsDisplay or setNeedsDisplayInRect: method of the view. These methods let the system know that it should update the view during the next drawing cycle. Because it waits until the next drawing cycle to update the view, you can call these methods on multiple views to update them at the same time.

Also, see these SO questions:

Community
  • 1
  • 1
Jean
  • 7,623
  • 6
  • 43
  • 58
  • I added a "NSLog(@"Count: %d", [_lines count]);" to the drawRect before the for-loop, it appears a couple of times before adding a line - then one time again & after that none times.. – Christian 'fuzi' Orgler Apr 04 '13 at 20:32
  • @Christian'fuzi'Orgler Once you have called `setNeedsDisplay` the system won't take any other call into account until the view is redrawn (*i.e. until your application main loop runs*). This is because you already made it clear the view should be redrawn **next time the modal loop runs**. If you want the view to be redrawn each time you call `setNeedsDisplay`, use the code provided in my answer. – Jean Apr 04 '13 at 20:35
  • Note: beware that calling `setNeedsDisplay` in `drawRect:` can lead to cyclic calls resulting in really bad performances. – Jean Apr 04 '13 at 20:41
2

If you need an animation - start a timer, once it's fired - adjust whatever line parameter you want and call setNeedsDisplay

Gobra
  • 4,263
  • 2
  • 15
  • 20