2

Note: as of iOS7 this problem may only manifest in the simulator -- still testing.

I have an implementation of CADisplayLink, and I need the code to run if and only if the display actually refreshes

This doesn't happen.

Here's a very simple test program:

I start the display link running; in the first frame aLabel should display "WordFlash"; for the next 19 frames it should display "--------" and for the next 100 it should be blank; then the cycle should repeat.

Occasionally (say 1 in 8 times), and unpredictably, the screen won't refresh to display "WordFlash" though the code has indeed fired (as the counter has advanced). I need the counter to advance only if "WordFlash" has successfully displayed for exactly 1 frame.

Any ideas? I am totally stumped.

Note: This display-refresh-skipping seems to happen without correlation to the time it takes the device to execute the simple code (as in the NSLogged time-to-execute the code can be identical in two different cycles while only one cycle has successfully flashed the word.

    #import "HomePwnerViewController.h"


@interface HomePwnerViewController ()

@end

@implementation HomePwnerViewController {
    int counter;
    UILabel *aLabel;

}

- (void)viewDidLoad
{
    [super viewDidLoad];

    aLabel = [[UILabel alloc] initWithFrame:self.view.frame];

    [self.view addSubview:aLabel];

    aLabel.alpha = 1;

    aLabel.text = @"";

    aLabel.textAlignment = NSTextAlignmentCenter;

    [aLabel setFont:[UIFont fontWithName:@"Courier" size:50]];

  [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(testWordFlashMethod) userInfo:nil repeats:NO];

}

- (void) displaySensitiveProcedure: (CADisplayLink *) sender {

    CFTimeInterval startTime = CACurrentMediaTime();


    if (counter == 0)
        aLabel.text = @"FlashWord";
    else if (counter < 20)
        aLabel.text = @"---------";
    else
        aLabel.text = @"";

    CFTimeInterval endTime = CACurrentMediaTime();
    if (counter == 0)
        NSLog(@"frame time %f frame length %f ratio %f", endTime - startTime, sender.duration, (endTime - startTime)/sender.duration);

    counter++;

    if (counter == 120)
        counter = 0;
}

- (void) testWordFlashMethod {
    CADisplayLink *DL = [CADisplayLink displayLinkWithTarget:self selector:@selector(displaySensitiveProcedure:)];

    DL.frameInterval = 1;

    counter = 0;

    [DL addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];   
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

Much obliged,

b

iambsoft
  • 21
  • 2

2 Answers2

1

If I were to guess, I'd say that animating a UILabel on screen refresh is not the best way to do things. In my experience changing the text on them can take a bit of time to render. If you want to draw a dash across the screen, you might be better served by using Core Graphics API's, or a CALayer based drawing approach.

ModernCarpentry
  • 3,127
  • 2
  • 20
  • 14
  • Yeah, the point isn't to produce the animation as described. This is just isolating the problem that I was experiencing in a much larger program. IE my end-goal is not to animate a dash going across the screen but instead to trigger code if and only if a uilabel successfully redraws. – iambsoft Jun 11 '13 at 04:46
0

Your displaySensitiveProcedure is probably taking more than a single frame (16.7ms) to update the screen, which makes it skip a frame. If you consistently take more than that (say 20ms), you can have the DisplayLink fire less frequently to get smooth animation at a slower framerate - see the frameInterval property. I would suggest timing your callback by doing something like this:

- (void) displaySensitiveProcedure: (CADisplayLink *) sender { 
  CFTimeInterval startTime = CACurrentMediaTime();

  ... <code> ...

  CFTimeInterval endTime = CACurrentMediaTime();
  NSLog(@"frame time: %f", endTime - startTime);
}
escrafford
  • 2,373
  • 16
  • 19
  • Good idea to test the code execution time. But code execution is taking place (or logged frame time) is taking place in .00002 to .00005 seconds, so the screen should have had plenty of time to get the changes into place, no? – iambsoft Jun 13 '13 at 16:36
  • Yeah, that seems good and fast. Perhaps there is drawing from UIKit stuff happening outside of this callback? Any time I've used CADisplayLink, I've been manually drawing/moving CALayers. – escrafford Jun 13 '13 at 19:01
  • also, don't nslog in the code being performance tested - nslog seems to take ~2ms for each call (on device), and totally kills performance. – escrafford Jun 13 '13 at 19:05