I want to switch the image shown in an NSImageView
, but I want to animate that change. I've tried various methods to do this. Hopefully one of you could suggest one that might actually work. I'm working with Cocoa for Mac.
views:
203answers:
2
A:
As far as I know, NSImageView
doesn't support animating image changes. However, you can place a second NSImageView
on top of the first one and animate hiding the old one and showing the new one. For example:
NSImageView *newImageView = [[NSImageView alloc] initWithFrame: [imageView frame]];
[newImageView setImageFrameStyle: [imageView imageFrameStyle]];
// anything else you need to copy properties from the old image view
// ...or unarchive it from a nib
[newImageView setImage: [NSImage imageNamed: @"NSAdvanced"]];
[[imageView superview] addSubview: newImageView
positioned: NSWindowAbove relativeTo: imageView];
[newImageView release];
NSDictionary *fadeIn = [NSDictionary dictionaryWithObjectsAndKeys:
newImageView, NSViewAnimationTargetKey,
NSViewAnimationFadeInEffect, NSViewAnimationEffectKey,
nil];
NSDictionary *fadeOut = [NSDictionary dictionaryWithObjectsAndKeys:
imageView, NSViewAnimationTargetKey,
NSViewAnimationFadeOutEffect, NSViewAnimationEffectKey,
nil];
NSViewAnimation *animation = [[NSViewAnimation alloc] initWithViewAnimations:
[NSArray arrayWithObjects: fadeOut, fadeIn, nil]];
[animation setAnimationBlockingMode: NSAnimationBlocking];
[animation setDuration: 2.0];
[animation setAnimationCurve: NSAnimationEaseInOut];
[animation startAnimation];
[imageView removeFromSuperview];
imageView = newImageView;
[animation release];
If your view is big and you can require 10.5+, then you could do the same thing with Core Animation, which will be hardware accelerated and use a lot less CPU.
After creating newImageView, do something like:
[newImageView setAlphaValue: 0];
[newImageView setWantsLayer: YES];
// ...
[self performSelector: @selector(animateNewImageView:) withObject: newImageView afterDelay: 0];
- (void)animateNewImageView:(NSImageView *)newImageView;
{
[NSAnimationContext beginGrouping];
[[NSAnimationContext currentContext] setDuration: 2];
[[newImageView animator] setAlphaValue: 1];
[[imageView animator] setAlphaValue: 0];
[NSAnimationContext endGrouping];
}
You'll need to modify the above to be abortable, but I'm not going to write all your code for you :-)
Nicholas Riley
2010-05-09 01:59:31
I want to be able to force the animation to stop in the middle; is that possible?
Alexsander Akers
2010-05-09 02:45:57
Sure, if you use NSAnimationNonblocking or NSAnimationNonblockingThreaded and keep a reference to the NSViewAnimation object somewhere, you can invoke [animation stopAnimation] then put the views back whichever way you want.
Nicholas Riley
2010-05-09 02:47:32
Okay, I tried to do that, but it was really choppy. Do you know how to do Rob Keniger's method? That seems to be the simplest.
Alexsander Akers
2010-05-09 02:56:59
I notice NSAnimationNonblockingThreaded seems smoother than NSAnimationNonblocking (makes sense since it isn't runloop based). If it's still slow with NSAnimationNonblockingThreaded, what size image are you trying to animate?
Nicholas Riley
2010-05-09 03:01:19
Do you know how to do Rob Keniger's method?
Alexsander Akers
2010-05-09 03:05:58
Well, sure, but you could also try a hybrid method. I'll update my solution in a second.
Nicholas Riley
2010-05-09 03:09:50
Thanks so much!
Alexsander Akers
2010-05-09 03:52:17
+3
A:
You could implement your own custom view that uses a Core Animation CALayer
to store the image. When you set the contents
property of the layer, the image will automatically smoothly animate from the old image to the new one.
Rob Keniger
2010-05-09 02:29:00