Craig Koster
Craig Koster

Reputation: 496

Cocoa Image Gallery Window

I have a HUD panel in an app and I want to be able to take a set of images and show each image on the panel for a few seconds before displaying the next image. I'm very new to Cocoa and am having trouble implementing this so some pointers would be welcomed. Here's what I'm currently trying:

[newShots enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) 
 {
     //get url
     NSURL *imageUrl = [[NSURL alloc] initWithString:[obj objectForKey:@"image_url"]];;

     //get image from url
     NSImage *image = [[NSImage alloc] initWithContentsOfURL:imageUrl];

     //set it to shot panel
     [shotPanelImageView setImage:image]; 

     //clean up
     [imageUrl release];
     [image release];

     //set needs refresh
     [shotPanelImageView setNeedsDisplay:YES];

     //sleep a few before moving to next
     [NSThread sleepForTimeInterval:3.0];
 }]; 

As you can see I'm just looping the info for each image, grabbing it via URL, setting it to the view, and then calling thread sleep for a few seconds before moving on. The issue is that the view will not redraw with the new image when it is assigned. I thought that setNeedsDisplay:YES would force a redraw but only the first image in the collection is ever displayed. I've put in NSLog()'s and debugged and I know for sure the enumeration is working correctly as I can see the new image information being set as it should.

Is there something I'm missing or is this a completely wrong way of going about solving this problem?

Thanks,

Craig

Upvotes: 0

Views: 839

Answers (1)

jscs
jscs

Reputation: 64002

You are sleeping the main thread, which I'm pretty sure is not a good idea. I suggest that the Cocoa way to do what you want is to use a timer. In place of your code above:

[NSTimer scheduledTimerWithTimeInterval:3.0
                                 target:self
                               selector:@selector(showNextShot:)
                               userInfo:nil
                                repeats:YES];

(The userInfo parameter allows you to pass along an arbitrary object that you want to use when the timer fires, so you could potentially use this to keep track of the current index as an NSNumber, but it would have to be wrapped in a mutable container object, because you can't set it later.)

Then put the code from your block into the method called by the timer. You'll need to make an instance variable for the current index.

- (void)showNextShot:(NSTimer *)timer {
    if( currentShotIdx >= [newShots count] ){
        [timer invalidate]; // Stop the timer
        return;    // Also call another cleanup method if needed
    }
    NSDictionary * obj = [newShots objectAtIndex:currentShotIdx];
    // Your code...
    currentShotIdx++;
}

To avoid the initial 3 sec delay caused by using a timer, you can call the same method your timer uses, right before you set it up:

[self showNextShot:nil]
[NSTimer scheduled...

Or could also schedule a non-repeating timer to fire as soon as possible (if you really wanted to use userInfo):

[NSTimer scheduledTimerWithTimeInterval:0.0
                                      ...
                                repeats:NO];

EDIT: I forgot about -initWithFireDate:interval:target:selector:userInfo:repeats:!

NSTimer *tim = [[NSTimer alloc] initWithFireDate:[NSDate date]
                                        interval:3.0
                                          target:self
                                        selector:@selector(showNextShot:)
                                        userInfo:nil
                                         repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:tim 
                             forMode:NSDefaultRunLoopMode];
[tim release];

Upvotes: 1

Related Questions