Hi,
Here is the sample code for "Mask Animation" in Objective C and iPhone/iPad/iPod.
#import <UIKit/UIKit.h>
typedef void (^animationCompletionBlock)(void);
#define kAnimationCompletionBlock @"animationCompletionBlock"
@interface ViewController : UIViewController
{
BOOL animationInFlight;
IBOutlet UIImageView *imageView;
}
@property (nonatomic) BOOL animationInFlight;
- (IBAction)MaskAnimation:(id)sender;
@end
#import <QuartzCore/QuartzCore.h>
#import "ViewController.h"
@interface ViewController ()
- (void) removePauseForLayer: (CALayer *) theLayer;
@end
@implementation ViewController
@synthesize animationInFlight;
#pragma mark - view lifecycle methods
- (void)viewDidLoad
{
[super viewDidLoad];
}
//We only support portrait and portrait upside down orientations
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (void)viewDidUnload
{
imageView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#pragma mark - Instance methods
- (IBAction)MaskAnimation:(id)sender;
{
animationCompletionBlock theBlock;
imageView.hidden = FALSE;//Show the image view
//Create a shape layer that we will use as a mask for the image view
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGFloat maskHeight = imageView.layer.bounds.size.height;
CGFloat maskWidth = imageView.layer.bounds.size.width;
CGPoint centerPoint;
centerPoint = CGPointMake( maskWidth/2, maskHeight/2);
//Make the radius of our arc large enough to reach into the corners of the image view.
CGFloat radius = sqrtf(maskWidth * maskWidth + maskHeight * maskHeight)/2;
// CGFloat radius = MIN(maskWidth, maskHeight)/2;
//Don't fill the path, but stroke it in black.
maskLayer.fillColor = [[UIColor clearColor] CGColor];
maskLayer.strokeColor = [[UIColor blackColor] CGColor];
maskLayer.lineWidth = radius; //Make the line thick enough to completely fill the circle we're drawing
// maskLayer.lineWidth = 10; //Make the line thick enough to completely fill the circle we're drawing
CGMutablePathRef arcPath = CGPathCreateMutable();
//Move to the starting point of the arc so there is no initial line connecting to the arc
CGPathMoveToPoint(arcPath, nil, centerPoint.x, centerPoint.y-radius/2);
//Create an arc at 1/2 our circle radius, with a line thickess of the full circle radius
CGPathAddArc(arcPath,
nil,
centerPoint.x,
centerPoint.y,
radius/2,
3*M_PI/2,
-M_PI/2,
NO);
maskLayer.path = arcPath;
//Start with an empty mask path (draw 0% of the arc)
maskLayer.strokeEnd = 0.0;
CFRelease(arcPath);
//Install the mask layer into out image view's layer.
imageView.layer.mask = maskLayer;
//Set our mask layer's frame to the parent layer's bounds.
imageView.layer.mask.frame = imageView.layer.bounds;
//Create an animation that increases the stroke length to 1, then reverses it back to zero.
CABasicAnimation *swipe = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
swipe.duration = 2;
swipe.delegate = self;
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
swipe.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
swipe.fillMode = kCAFillModeForwards;
swipe.removedOnCompletion = NO;
swipe.autoreverses = YES;
swipe.toValue = [NSNumber numberWithFloat: 1.0];
self.animationInFlight = TRUE;
//Set up a completion block that will be called once the animation is completed.
theBlock = ^void(void)
{
imageView.layer.mask = nil;
self.animationInFlight = FALSE;
imageView.hidden = TRUE;
if (self.view.layer.speed == 0)
[self removePauseForLayer: self.view.layer];
};
/*
Install the completion block in the animation using the key kAnimationCompletionBlock
The completion block will be run by in the animation's animationDidStop:finished delegate method.
This approach doesn't work for animations that are part of a group, unfortunately, since an animation's
delegate methods don't get called when the animation is part of an animation group
*/
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
[maskLayer addAnimation: swipe forKey: @"strokeEnd"];
}
- (void) removePauseForLayer: (CALayer *) theLayer;
{
theLayer.speed = 1.0;
theLayer.timeOffset = 0.0;
theLayer.beginTime = 0.0;
}
#pragma mark - CAAnimation delegate methods
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
if (theBlock)
theBlock();
}
@end
Here is the sample code for "Mask Animation" in Objective C and iPhone/iPad/iPod.
#import <UIKit/UIKit.h>
typedef void (^animationCompletionBlock)(void);
#define kAnimationCompletionBlock @"animationCompletionBlock"
@interface ViewController : UIViewController
{
BOOL animationInFlight;
IBOutlet UIImageView *imageView;
}
@property (nonatomic) BOOL animationInFlight;
- (IBAction)MaskAnimation:(id)sender;
@end
#import <QuartzCore/QuartzCore.h>
#import "ViewController.h"
@interface ViewController ()
- (void) removePauseForLayer: (CALayer *) theLayer;
@end
@implementation ViewController
@synthesize animationInFlight;
#pragma mark - view lifecycle methods
- (void)viewDidLoad
{
[super viewDidLoad];
}
//We only support portrait and portrait upside down orientations
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (void)viewDidUnload
{
imageView = nil;
[super viewDidUnload];
// Release any retained subviews of the main view.
}
#pragma mark - Instance methods
- (IBAction)MaskAnimation:(id)sender;
{
animationCompletionBlock theBlock;
imageView.hidden = FALSE;//Show the image view
//Create a shape layer that we will use as a mask for the image view
CAShapeLayer *maskLayer = [CAShapeLayer layer];
CGFloat maskHeight = imageView.layer.bounds.size.height;
CGFloat maskWidth = imageView.layer.bounds.size.width;
CGPoint centerPoint;
centerPoint = CGPointMake( maskWidth/2, maskHeight/2);
//Make the radius of our arc large enough to reach into the corners of the image view.
CGFloat radius = sqrtf(maskWidth * maskWidth + maskHeight * maskHeight)/2;
// CGFloat radius = MIN(maskWidth, maskHeight)/2;
//Don't fill the path, but stroke it in black.
maskLayer.fillColor = [[UIColor clearColor] CGColor];
maskLayer.strokeColor = [[UIColor blackColor] CGColor];
maskLayer.lineWidth = radius; //Make the line thick enough to completely fill the circle we're drawing
// maskLayer.lineWidth = 10; //Make the line thick enough to completely fill the circle we're drawing
CGMutablePathRef arcPath = CGPathCreateMutable();
//Move to the starting point of the arc so there is no initial line connecting to the arc
CGPathMoveToPoint(arcPath, nil, centerPoint.x, centerPoint.y-radius/2);
//Create an arc at 1/2 our circle radius, with a line thickess of the full circle radius
CGPathAddArc(arcPath,
nil,
centerPoint.x,
centerPoint.y,
radius/2,
3*M_PI/2,
-M_PI/2,
NO);
maskLayer.path = arcPath;
//Start with an empty mask path (draw 0% of the arc)
maskLayer.strokeEnd = 0.0;
CFRelease(arcPath);
//Install the mask layer into out image view's layer.
imageView.layer.mask = maskLayer;
//Set our mask layer's frame to the parent layer's bounds.
imageView.layer.mask.frame = imageView.layer.bounds;
//Create an animation that increases the stroke length to 1, then reverses it back to zero.
CABasicAnimation *swipe = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
swipe.duration = 2;
swipe.delegate = self;
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
swipe.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
swipe.fillMode = kCAFillModeForwards;
swipe.removedOnCompletion = NO;
swipe.autoreverses = YES;
swipe.toValue = [NSNumber numberWithFloat: 1.0];
self.animationInFlight = TRUE;
//Set up a completion block that will be called once the animation is completed.
theBlock = ^void(void)
{
imageView.layer.mask = nil;
self.animationInFlight = FALSE;
imageView.hidden = TRUE;
if (self.view.layer.speed == 0)
[self removePauseForLayer: self.view.layer];
};
/*
Install the completion block in the animation using the key kAnimationCompletionBlock
The completion block will be run by in the animation's animationDidStop:finished delegate method.
This approach doesn't work for animations that are part of a group, unfortunately, since an animation's
delegate methods don't get called when the animation is part of an animation group
*/
[swipe setValue: theBlock forKey: kAnimationCompletionBlock];
[maskLayer addAnimation: swipe forKey: @"strokeEnd"];
}
- (void) removePauseForLayer: (CALayer *) theLayer;
{
theLayer.speed = 1.0;
theLayer.timeOffset = 0.0;
theLayer.beginTime = 0.0;
}
#pragma mark - CAAnimation delegate methods
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
animationCompletionBlock theBlock = [theAnimation valueForKey: kAnimationCompletionBlock];
if (theBlock)
theBlock();
}
@end
No comments:
Post a Comment