// // ZoomLeopard.m // ZoomCocoa // // Created by Andrew Hunter on 28/10/2007. // Copyright 2007 Andrew Hunter. All rights reserved. // #import "ZoomLeopard.h" #import @implementation ZoomLeopard - (id) init { self = [super init]; if (self) { NSLog(@"Leopard Extensions loaded"); animationsWillFinish = [[NSMutableArray alloc] init]; finishInvocations = [[NSMutableArray alloc] init]; } return self; } - (void) prepareToAnimateView: (NSView*) view layer: (CALayer*) layer { NSEnumerator* subviewEnum = [[view subviews] objectEnumerator]; NSView* subview; while (subview = [subviewEnum nextObject]) { [self prepareToAnimateView: subview layer: nil]; } if (![view wantsLayer]) { [view setWantsLayer: YES]; } [view layer].backgroundColor = CGColorCreateGenericRGB(0, 0,0,0); } - (void) prepareToAnimateView: (NSView*) view { CALayer* viewLayer = [CALayer layer]; viewLayer.backgroundColor = CGColorCreateGenericRGB(0, 0,0,0); [self prepareToAnimateView: view layer: viewLayer]; } - (void) popView: (NSView*) view duration: (NSTimeInterval) seconds finished: (NSInvocation*) finished { // Set up the layers for this view [self prepareToAnimateView: view]; // Create a pop-up animation CABasicAnimation* popAnimation = [CABasicAnimation animation]; CATransform3D startScaling = CATransform3DScale(CATransform3DIdentity, 0.2, 0.2, 0.2); CATransform3D finalScaling = CATransform3DIdentity; CATransform3D popScaling = CATransform3DScale(CATransform3DIdentity, 1.1, 1.1, 1.1); popAnimation.keyPath = @"transform"; popAnimation.fromValue = [NSValue valueWithCATransform3D: startScaling]; popAnimation.toValue = [NSValue valueWithCATransform3D: popScaling]; popAnimation.duration = seconds * 0.8; popAnimation.beginTime = CACurrentMediaTime(); popAnimation.repeatCount = 1; popAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; CABasicAnimation* popBackAnimation = [CABasicAnimation animation]; popBackAnimation.keyPath = @"transform"; popBackAnimation.fromValue = [NSValue valueWithCATransform3D: popScaling]; popBackAnimation.toValue = [NSValue valueWithCATransform3D: finalScaling]; popBackAnimation.duration = seconds * 0.2; popBackAnimation.beginTime = CACurrentMediaTime() + seconds * 0.8; popBackAnimation.repeatCount = 1; popBackAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; // Create a fade-in animation CABasicAnimation* fadeAnimation = [CABasicAnimation animation]; fadeAnimation.keyPath = @"opacity"; fadeAnimation.fromValue = [NSNumber numberWithDouble: 0]; fadeAnimation.toValue = [NSNumber numberWithDouble: 1]; fadeAnimation.repeatCount = 1; fadeAnimation.timingFunction= [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; fadeAnimation.duration = seconds*0.5; fadeAnimation.fillMode = kCAFillModeBoth; popBackAnimation.delegate = self; // Animate the view's layer [view layer].opacity = 1; [[view layer] removeAllAnimations]; [[view layer] addAnimation: popAnimation forKey: @"PopView"]; [[view layer] addAnimation: popBackAnimation forKey: @"PopBackView"]; [[view layer] addAnimation: fadeAnimation forKey: @"FadeView"]; // Set up the finished event handler if (finished) { [animationsWillFinish addObject: [[view layer] animationForKey: @"PopBackView"]]; [finishInvocations addObject: finished]; } } - (void) popOutView: (NSView*) view duration: (NSTimeInterval) seconds finished: (NSInvocation*) finished { // Set up the layers for this view [self prepareToAnimateView: view]; // Create a pop-up animation CABasicAnimation* popAnimation = [CABasicAnimation animation]; CATransform3D startScaling = CATransform3DScale(CATransform3DIdentity, 0.2, 0.2, 0.2); CATransform3D finalScaling = CATransform3DIdentity; CATransform3D popScaling = CATransform3DScale(CATransform3DIdentity, 1.1, 1.1, 1.1); popAnimation.keyPath = @"transform"; popAnimation.fromValue = [NSValue valueWithCATransform3D: finalScaling]; popAnimation.toValue = [NSValue valueWithCATransform3D: popScaling]; popAnimation.duration = seconds * 0.2; popAnimation.beginTime = CACurrentMediaTime(); popAnimation.repeatCount = 1; popAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; popAnimation.fillMode = kCAFillModeBoth; CABasicAnimation* popBackAnimation = [CABasicAnimation animation]; popBackAnimation.keyPath = @"transform"; popBackAnimation.fromValue = [NSValue valueWithCATransform3D: popScaling]; popBackAnimation.toValue = [NSValue valueWithCATransform3D: startScaling]; popBackAnimation.duration = seconds * 0.8; popBackAnimation.beginTime = CACurrentMediaTime() + seconds * 0.2; popBackAnimation.repeatCount = 1; popBackAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; popBackAnimation.fillMode = kCAFillModeBoth; // Create a fade-in animation CABasicAnimation* fadeAnimation = [CABasicAnimation animation]; fadeAnimation.keyPath = @"opacity"; fadeAnimation.fromValue = [NSNumber numberWithDouble: 1]; fadeAnimation.toValue = [NSNumber numberWithDouble: 0]; fadeAnimation.repeatCount = 1; fadeAnimation.timingFunction= [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; fadeAnimation.duration = seconds; fadeAnimation.fillMode = kCAFillModeBoth; fadeAnimation.delegate = self; // Animate the view's layer [view layer].opacity = 0; [[view layer] removeAllAnimations]; [[view layer] addAnimation: popAnimation forKey: @"PopView"]; [[view layer] addAnimation: popBackAnimation forKey: @"PopBackView"]; [[view layer] addAnimation: fadeAnimation forKey: @"FadeView"]; // Set up the finished event handler if (finished) { [animationsWillFinish addObject: [[view layer] animationForKey: @"FadeView"]]; [finishInvocations addObject: finished]; fadeAnimation.delegate = self; } } - (void) clearLayersForView: (NSView*) view { if ([view wantsLayer]) { [view setWantsLayer: NO]; NSEnumerator* subviewEnum = [[view subviews] objectEnumerator]; NSView* subview; while (subview = [subviewEnum nextObject]) { [self clearLayersForView: subview]; } } } - (void) removeLayerForView: (NSView*) view { [view setWantsLayer: NO]; } - (void) fullScreenView: (NSView*) view // Animates a view to full screen fromFrame: (NSRect) oldWindowFrame toFrame: (NSRect) newWindowFrame { [view setWantsLayer: YES]; [[view layer] display]; float ratioX = oldWindowFrame.size.width / newWindowFrame.size.width; float ratioY = oldWindowFrame.size.height / newWindowFrame.size.height; CATransform3D scaleFrom = CATransform3DIdentity; scaleFrom = CATransform3DScale(scaleFrom, ratioX, ratioY, 1.0); scaleFrom = CATransform3DTranslate(scaleFrom, oldWindowFrame.size.width - newWindowFrame.size.width, oldWindowFrame.size.height - newWindowFrame.size.height, 0.0); scaleFrom = CATransform3DTranslate(scaleFrom, (oldWindowFrame.origin.x - newWindowFrame.origin.x)/ratioX, (oldWindowFrame.origin.y - newWindowFrame.origin.y)/ratioY, 0.0); CABasicAnimation* scaleAnimation = [CABasicAnimation animation]; scaleAnimation.keyPath = @"transform"; scaleAnimation.fromValue = [NSValue valueWithCATransform3D: scaleFrom]; scaleAnimation.toValue = [NSValue valueWithCATransform3D: CATransform3DIdentity]; scaleAnimation.duration = 0.35; scaleAnimation.repeatCount = 1; scaleAnimation.timingFunction = [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseInEaseOut]; scaleAnimation.delegate = self; // Animate the view's layer [[view layer] removeAllAnimations]; [[view layer] addAnimation: scaleAnimation forKey: @"ScaleView"]; scaleAnimation = (CABasicAnimation*)[[view layer] animationForKey: @"ScaleView"]; // Invocations don't work when this is loaded as a bundle (no idea why: appears to be an Apple // bug). [animationsWillFinish addObject: scaleAnimation]; [finishInvocations addObject: view]; } // = Animation delegate functions = - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { int index = [animationsWillFinish indexOfObject: theAnimation]; while (index != NSNotFound) { id invocation = [finishInvocations objectAtIndex: index]; if ([invocation isKindOfClass: [NSInvocation class]]) { [invocation invoke]; } else if ([invocation isKindOfClass: [NSView class]]) { [invocation setWantsLayer: NO]; } [animationsWillFinish removeObjectAtIndex: index]; [finishInvocations removeObjectAtIndex: index]; index = [animationsWillFinish indexOfObject: theAnimation]; } } @end