All images appear/animate out at once?


I’ve finished the chapter but seem to have done something wrong. The images, whether allowedFiles is used or not, all appear and animate at once.

This behavior seemed to me like what I would observe if the [CATransaction begin] and [CATransaction commit] calls were around the addImagesFromFolderURL loop rather than inside it. So I tried this (commenting those calls out inside presentImage) and did indeed observe the same behavior. Then I just commented out all calls to [CATransaction begin] and [CATransaction commit] and observed the exact same behavior. It’s as if the begin and commit calls to CATransaction are having no effect.

I’ve included my implementations of addImagesFromFolderURL and presentImage below. You’ll note that I’ve added a log message in addImagesFromFolderURL loop to track it’s progress. During a run, I see these log messages scrolling by, but the Scattered window stays blank until the very end and then all the images appear and animate at once.

Any suggestions? Is there something I’ve done incorrectly?

- (void)addImagesFromFolderURL:(NSURL *)folderURL
    NSTimeInterval t0 = [NSDate timeIntervalSinceReferenceDate];
    NSFileManager *fileMgr = [NSFileManager defaultManager];
    NSDirectoryEnumerator *dirEnum = [fileMgr enumeratorAtURL:folderURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:nil];
    for(NSURL *url in dirEnum) {
        // skip directories
        NSNumber *isDirectory = nil;
        [url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:nil];
        if([isDirectory boolValue])
        NSImage *img = [[NSImage alloc] initWithContentsOfURL:url];
        NSImage *thumbImg = [self thumbImageFromImage:img];
        [self presentImage];
        NSString *dt = [NSString stringWithFormat:@"%0.1fs",
                       [NSDate timeIntervalSinceReferenceDate]-t0];
        [self setText:dt];
        NSLog(@"Presented %@ in %@ sec",url,dt);

- (void)presentImage:(NSImage *)image
    CGRect superlayerBounds = view.layer.bounds;
    NSPoint center = NSMakePoint(CGRectGetMidX(superlayerBounds), CGRectGetMidY(superlayerBounds));
    NSRect imageBounds = NSMakeRect(0, 0, image.size.width, image.size.height);
    CGPoint randomPoint = CGPointMake(CGRectGetMaxX(superlayerBounds)*(double)random()/(double)RAND_MAX, CGRectGetMaxY(superlayerBounds)*(double)random()/(double)RAND_MAX);
    CAMediaTimingFunction *tf = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    CABasicAnimation *posAnim = [CABasicAnimation animation];
    posAnim.fromValue = [NSValue valueWithPoint:center];
    posAnim.duration = 1.5;
    posAnim.timingFunction = tf;
    CABasicAnimation *bdsAnim = [CABasicAnimation animation];
    bdsAnim.fromValue = [NSValue valueWithRect:NSZeroRect];
    bdsAnim.duration = 1.5;
    bdsAnim.timingFunction = tf;
    CALayer *layer = [CALayer layer];
    layer.contents = image;
    layer.actions = [NSDictionary dictionaryWithObjectsAndKeys:posAnim,@"position", bdsAnim, @"bounds", nil];
    [CATransaction begin];
    [view.layer addSublayer:layer];
    layer.position = randomPoint;
    layer.bounds = NSRectToCGRect(imageBounds);
    [CATransaction commit];


For anyone interested, I posted on stackoverflow and Adam has been very helpfully responding to
the question there.