ACBurk
ACBurk

Reputation: 4428

View not Updating

Kinda new to iPhone programming and was experimenting with threads

- (void)viewDidLoad {
    [super viewDidLoad];

    [NSThread detachNewThreadSelector:@selector(changeMain) toTarget:self withObject:nil];
    [NSThread detachNewThreadSelector:@selector(changeThread) toTarget:self withObject:nil];
}

- (void)changeMain{
    NSAutoreleasePool* arp = [[NSAutoreleasePool alloc] init];

    for (int i = 0; i < 1000000; i++) {
        [mainValue setText:[NSString stringWithFormat:@"%d",i]];
        [self.view setNeedsDisplay];    
    }

    [arp release];
}
- (void)changeThread{
    NSAutoreleasePool* arp = [[NSAutoreleasePool alloc] init];

    for (int i = 0; i < 1000000; i++) {
        [threadValue setText:[NSString stringWithFormat:@"%d",i]];
        [self.view setNeedsDisplay];
    }

    [arp release];
}

mainValue and threadValue are both just UILabels. I expected this to run and see both labels run up to 999999 but instead it starts at some low number (what it is when the screen initally refreshing i assume), pauses for a bit, then updates to 999999. I'm thinking the screen just isn't refreshing.

Is this correct? Am I doing it wrong?

Upvotes: 0

Views: 263

Answers (4)

mga
mga

Reputation: 1970

Don't use for() for animations. The for will process in the same "frame". Maybe just have an ivar i and in changeMain you can have if (i<10000) { mainValue.text = [NSString stringWithFormat:@"%d",i]; i++;} or something like that. This way the setText only happens once per "frame".

Upvotes: 1

Valerii Hiora
Valerii Hiora

Reputation: 1552

  1. You have to perform any Cocoa Touch operations in main thread, in other case results are unpredictable.
  2. You don't have to call setNeedsDisplay manually.

So I'd recommend to use the following construction:

[threadValue performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%d",i] waitUntilDone:YES];

Additional notes: 1. 100000 runs can overflow the main thread queue so some values will disappear 2. You can use waitUntilDone:NO too

Upvotes: 1

MrMage
MrMage

Reputation: 7487

I'm not sure if this will work, but you could try to force the setNeedsDisplay method to be executed on the main thread, using e.g. [self performSelectorOnMainThread:@selector(setNeedsDisplay) withObject:nil waitUntilDone:YES]. This should (hopefully, i didn't test it!) update the view after every increment. You could also try to set waitUntiDone:NO, but I'm unsure what will happen then.

See here

Upvotes: 0

rluba
rluba

Reputation: 2044

The setNeedsDisplay message triggers a redraw, but it only happens during the next time the main thread becomes active. So your side threads trigger a million redraws but they are queued. As soon as the main thread continues, it "collapses" all requests into one redraw.

Most likely setNeedsDisplay just sets a flag that is checked once during each run of the main loop, so setting it 1000000 to true doesn't influence anything. Try to let the "worker threads" sleep after each iteration to give the main thread some time to redraw.

Upvotes: 1

Related Questions