Cocoa notes (24) Animation
六月 2nd, 2011
Cocoa
动画主要分两种实现方式一种是UIView层面上,一种是底层的CATransition
1 2 3 4 5 6 7 8 9 | // Why 0.3f seconds? Because that is the time used to display a keyboard CGContextRef context = UIGraphicsGetCurrentContext(); [UIView beginAnimations:nil context:context]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:0.3f]; // change location/size/transparent.. [UIView commitAnimations]; |
变形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CGFloat angle = theta * (M_PI / 100.0f); CGAffineTransform transform = CGAffineTransformMakeRotation(angle); // Theta ranges between 0% and 199% of PI, i.e. between 0 and 2*PI theta = (theta + 1) % 200; // For fun, scale by the absolute value of the cosine float degree = cos(angle); if (degree < 0.0) degree *= -1.0f; degree += 0.5f; // Create add scaling to the rotation transform CGAffineTransform scaled = CGAffineTransformScale(transform, degree, degree); // Apply the affine transform [[self.view viewWithTag:999] setTransform:scaled]; |
Swap(互换效果)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // hide the button self.navigationItem.rightBarButtonItem = nil; UIView *frontObject = [[self.view subviews] objectAtIndex:2]; UIView *backObject = [[self.view subviews] objectAtIndex:1]; CGContextRef context = UIGraphicsGetCurrentContext(); [UIView beginAnimations:nil context:context]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:1.0]; frontObject.alpha = 0.0f; backObject.alpha = 1.0f; frontObject.transform = CGAffineTransformMakeScale(0.25f, 0.25f); backObject.transform = CGAffineTransformIdentity; [self.view exchangeSubviewAtIndex:1 withSubviewAtIndex:2]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animationFinished:)]; [UIView commitAnimations]; |
Flip(翻转)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | CGContextRef context = UIGraphicsGetCurrentContext(); [UIView beginAnimations:nil context:context]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:1.0]; UIView *whiteBackdrop = [self.view viewWithTag:100]; // Choose left or right flip if ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex]) [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:whiteBackdrop cache:YES]; else [UIView setAnimationTransition: UIViewAnimationTransitionFlipFromRight forView:whiteBackdrop cache:YES]; NSInteger purple = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:999]]; NSInteger maroon = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:998]]; [whiteBackdrop exchangeSubviewAtIndex:purple withSubviewAtIndex:maroon]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animationFinished:)]; [UIView commitAnimations]; |
CAAnimation 的delegate方法(竟然没有相应的protocol??)
– animationDidStart: delegate method
– animationDidStop:finished: delegate method
CATransition
4种动画形式
NSString * const kCATransitionFade;
NSString * const kCATransitionMoveIn;
NSString * const kCATransitionPush;
NSString * const kCATransitionReveal;
4个方向
NSString * const kCATransitionFromRight;
NSString * const kCATransitionFromLeft;
NSString * const kCATransitionFromTop;
NSString * const kCATransitionFromBottom;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // Set up the animation CATransition *animation = [CATransition animation]; animation.delegate = self; animation.duration = 1.0f; animation.timingFunction = UIViewAnimationCurveEaseInOut; switch ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex]) { case 0: animation.type = kCATransitionFade; break; case 1: animation.type = kCATransitionMoveIn; break; case 2: animation.type = kCATransitionPush; break; case 3: animation.type = kCATransitionReveal; default: br eak; } if (isLeft) animation.subtype = kCATransitionFromRight; else animation.subtype = kCATransitionFromLeft; // Perform the animation UIView *whitebg = [self.view viewWithTag:10]; NSInteger purple = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:99]]; NSInteger white = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:100]]; [whitebg exchangeSubviewAtIndex:purple withSubviewAtIndex:white]; [[whitebg layer] addAnimation:animation forKey:@"animation"]; |
CALayer (疑惑),animation 没有commit?或许是代码笔误。。
此例疑惑,为什么最后theView又出现了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // Adapted from Lucas Newman's sample code (www.lucasnewman.com) UIView *theView = [self.view viewWithTag:101]; [CATransaction begin]; [CATransaction setValue:[NSNumber numberWithFloat:ANIMATION_DURATION] forKey:kCATransactionAnimationDuration]; // scale it down CABasicAnimation *shrinkAnimation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; shrinkAnimation.delegate = self; shrinkAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; shrinkAnimation.toValue = [NSNumber numberWithFloat:0.0]; [[theView layer] addAnimation:shrinkAnimation forKey:@"shrinkAnimation"]; // fade it out CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:@"opacity"]; fadeAnimation.toValue = [NSNumber numberWithFloat:0.0]; fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; [[theView layer] addAnimation:fadeAnimation forKey:@"fadeAnimation"]; // make it jump a couple of times CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; CGMutablePathRef positionPath = CGAutorelease(CGPathCreateMutable()); CGPathMoveToPoint(positionPath, NULL, [theView layer].position.x, [theView layer].position.y); CGPathAddQuadCurveToPoint(positionPath, NULL, [theView layer].position.x, - [theView layer].position.y, [theView layer].position.x, [theView layer].position.y); CGPathAddQuadCurveToPoint(positionPath, NULL, [theView layer].position.x, - [theView layer].position.y * 1.5, [theView layer].position.x, [theView layer].position.y); CGPathAddQuadCurveToPoint(positionPath, NULL, [theView layer].position.x, - [theView layer].position.y * 1.25, [theView layer].position.x, [theView layer].position.y); positionAnimation.path = positionPath; positionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; [[theView layer] addAnimation:positionAnimation forKey:@"positionAnimation"]; [CATransaction commit]; |
curling(翻页效果)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | // hide the button self.navigationItem.rightBarButtonItem = nil; CGContextRef context = UIGraphicsGetCurrentContext(); [UIView beginAnimations:nil context:context]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:1.0]; UIView *whiteBackdrop = [self.view viewWithTag:100]; // Choose up or down curl if ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex]) [UIView setAnimationTransition: UIViewAnimationTransitionCurlUp forView:whiteBackdrop cache:YES]; else [UIView setAnimationTransition: UIViewAnimationTransitionCurlDown forView:whiteBackdrop cache:YES]; NSInteger purple = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:999]]; NSInteger maroon = [[whiteBackdrop subviews] indexOfObject:[whiteBackdrop viewWithTag:998]]; [whiteBackdrop exchangeSubviewAtIndex:purple withSubviewAtIndex:maroon]; [UIView setAnimationDelegate:self]; [UIView setAnimationDidStopSelector:@selector(animationFinished:)]; [UIView commitAnimations]; |
翻页控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | - (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag { self.navigationItem.rightBarButtonItem = BARBUTTON(@"Curl", @selector(curl:)); curlUp = !curlUp; } - (void) curl: (id) sender { // hide the button self.navigationItem.rightBarButtonItem = nil; // Set up the animation CATransition *animation = [CATransition animation]; animation.delegate = self; animation.duration = 0.3f; animation.timingFunction = UIViewAnimationCurveEaseInOut; animation.removedOnCompletion = NO; if (curlUp) { animation.type = @"pageCurl"; animation.fillMode = kCAFillModeForwards; animation.endProgress = 0.7; } else { animation.type = @"pageUnCurl"; animation.fillMode = kCAFillModeBackwards; animation.startProgress = 0.3; } // Perform the animation UIView *whitebg = [self.view viewWithTag:10]; NSInteger purple = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:99]]; NSInteger white = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:100]]; [whitebg exchangeSubviewAtIndex:purple withSubviewAtIndex:white]; [[whitebg layer] addAnimation:animation forKey:@"page curl"]; // Allow or disallow user interaction (otherwise you can touch "through" // the cover view to enable/disable the switch) if (purple < white) [self.view viewWithTag:99].userInteractionEnabled = YES; else [self.view viewWithTag:99].userInteractionEnabled = NO; } |
更多效果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // Set up the animation CATransition *animation = [CATransition animation]; animation.delegate = self; animation.duration = 1.0f; animation.timingFunction = UIViewAnimationCurveEaseInOut; switch ([(UISegmentedControl *)self.navigationItem.titleView selectedSegmentIndex]) { case 0: animation.type = @"rippleEffect"; break; case 1: animation.type = @"pageCurl"; break; case 2: animation.type = @"pageUnCurl"; break; case 3: animation.type = @"suckEffect"; break; default: break; } switch (direction) { case 0: animation.subtype = kCATransitionFromRight; break; case 1: animation.subtype = kCATransitionFromTop; break; case 2: animation.subtype = kCATransitionFromLeft; break; case 3: animation.subtype = kCATransitionFromBottom; break; default: break; } // Perform the animation UIView *whitebg = [self.view viewWithTag:10]; NSInteger purple = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:99]]; NSInteger white = [[whitebg subviews] indexOfObject:[whitebg viewWithTag:100]]; [whitebg exchangeSubviewAtIndex:purple withSubviewAtIndex:white]; [[whitebg layer] addAnimation:animation forKey:@"animation"]; if (++direction > 3) direction -= 4; |
有以下效果,奇怪为什么ios library里面找不到:
pageCurl 向上翻一页
pageUnCurl 向下翻一页
rippleEffect 滴水效果
suckEffect 收缩效果,如一块布被抽走
cube 立方体效果
oglFlip 上下翻转效果
Modal Animation(模态动画。动画会等前一段结束后再开始第二段)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | [self.view viewWithTag:101].alpha = 1.0f; // Bounce to 115% of the normal size [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:0.4f]; [self.view viewWithTag:101].transform = CGAffineTransformMakeScale(1.15f, 1.15f); [UIView commitModalAnimations]; // Return back to 100% [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:0.3f]; [self.view viewWithTag:101].transform = CGAffineTransformMakeScale(1.0f, 1.0f); [UIView commitModalAnimations]; // Pause for a second and appreciate the presentation [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0f]]; // Slowly zoom back down and hide the view [UIView beginAnimations:nil context:UIGraphicsGetCurrentContext()]; [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut]; [UIView setAnimationDuration:1.0f]; [self.view viewWithTag:101].transform = CGAffineTransformMakeScale(0.01f, 0.01f); [UIView commitModalAnimations]; |
逐帧动画
1 2 3 4 5 6 7 8 9 10 11 12 | // Load butterfly images NSMutableArray *bflies = [NSMutableArray array]; for (int i = 1; i <= 17; i++) [bflies addObject:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"bf_%d", i] ofType:@"png"]]]; UIImageView *butterflyView = [[UIImageView alloc] initWithFrame:CGRectMake(40.0f, 300.0f, 60.0f, 60.0f)]; butterflyView.tag = 300; butterflyView.animationImages = bflies; butterflyView.animationDuration = 0.75f; [self.view addSubview:butterflyView]; [butterflyView startAnimating]; [butterflyView release]; |
演示:两种不同的实现倒影的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #define CGAutorelease(x) (__typeof(x))[NSMakeCollectable(x) autorelease] #import "ImageHelper-Reflections.h" @implementation ImageHelper + (CGImageRef) createGradientImage: (CGSize)size { CGFloat colors[] = {0.0, 1.0, 1.0, 1.0}; // Create gradient in gray device color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); CGContextRef context = CGBitmapContextCreate(nil, size.width, size.height, 8, 0, colorSpace, kCGImageAlphaNone); CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2); CGColorSpaceRelease(colorSpace); // Draw the linear gradient CGPoint p1 = CGPointZero; CGPoint p2 = CGPointMake(0, size.height); CGContextDrawLinearGradient(context, gradient, p1, p2, kCGGradientDrawsAfterEndLocation); // Return the CGImage CGImageRef theCGImage = CGBitmapContextCreateImage(context); CFRelease(gradient); CGContextRelease(context); return theCGImage; } + (UIImage *) reflectionOfView: (UIView *)theView withPercent: (CGFloat) percent { // Retain the width but shrink the height CGSize size = CGSizeMake(theView.frame.size.width, theView.frame.size.height * percent); // Shrink the view UIGraphicsBeginImageContext(size); CGContextRef context = UIGraphicsGetCurrentContext(); [theView.layer renderInContext:context]; UIImage *partialimg = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); // build the mask CGImageRef mask = [ImageHelper createGradientImage:size]; CGImageRef ref = CGImageCreateWithMask(partialimg.CGImage, mask); UIImage *theImage = [UIImage imageWithCGImage:ref]; CGImageRelease(ref); CGImageRelease(mask); return theImage; } const CGFloat kReflectDistance = 10.0f; + (void) addReflectionToView: (UIView *) theView { theView.clipsToBounds = NO; UIImageView *reflection = [[UIImageView alloc] initWithImage:[ImageHelper reflectionOfView:theView withPercent: 0.45f]]; CGRect frame = reflection.frame; frame.origin = CGPointMake(0.0f, theView.frame.size.height + kReflectDistance); reflection.frame = frame; [theView addSubview:reflection]; [reflection release]; } const CGFloat kReflectPercent = 0.5f; const CGFloat kReflectOpacity = 0.5f; + (void) addSimpleReflectionToView: (UIView *) theView { CALayer *reflectionLayer = [CALayer layer]; reflectionLayer.contents = [theView layer].contents; reflectionLayer.opacity = kReflectOpacity; reflectionLayer.frame = CGRectMake(0.0f, 0.0f, theView.frame.size.width, theView.frame.size.height * kReflectPercent); CATransform3D stransform = CATransform3DMakeScale(1.0f, -1.0f, 1.0f); CATransform3D transform = CATransform3DTranslate(stransform, 0.0f, -(kReflectDistance + theView.frame.size.height), 0.0f); reflectionLayer.transform = transform; reflectionLayer.sublayerTransform = reflectionLayer.transform; [[theView layer] addSublayer:reflectionLayer]; } @end |
UIView
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState
如果没有停止(+ setAnimationDidStopSelector:)就开始下一段动画,将fromCurrentState设置为YES,会让动画从当前状态开始变化。
可能有用,留着到家看brow-leaves-67b10ed
