You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
11 KiB

//
// QCMethod.m
//
// www.quartzcodeapp.com
//
#import "QCMethod.h"
@implementation QCMethod
+ (CAAnimation*)reverseAnimation:(CAAnimation*)anim totalDuration:(CGFloat)totalDuration{
CGFloat duration = anim.duration + (anim.autoreverses ? anim.duration : 0);
duration = anim.repeatCount > 1 ? duration * anim.repeatCount : duration;
CGFloat endTime = anim.beginTime + duration;
CGFloat reverseStartTime = totalDuration - endTime;
CAAnimation *newAnim;
//Reverse timing function
void (^reverseTimingFunction)(CAAnimation*) = ^(CAAnimation *theAnim){
CAMediaTimingFunction *timingFunction = theAnim.timingFunction;
if (timingFunction) {
float first[2];
float second[2];
[timingFunction getControlPointAtIndex:1 values:first];
[timingFunction getControlPointAtIndex:2 values:second];
theAnim.timingFunction = [CAMediaTimingFunction functionWithControlPoints:1-second[0] :1-second[1] :1-first[0] :1-first[1]];
}
};
//Reverse animation values appropriately
if ([anim isKindOfClass:[CABasicAnimation class]]) {
CABasicAnimation *basicAnim = (CABasicAnimation*)anim;
if (!anim.autoreverses) {
id fromValue = basicAnim.toValue;
basicAnim.toValue = basicAnim.fromValue;
basicAnim.fromValue = fromValue;
reverseTimingFunction(basicAnim);
}
basicAnim.beginTime = reverseStartTime;
if (reverseStartTime > 0) {
CAAnimationGroup *groupAnim = [CAAnimationGroup animation];
groupAnim.animations = @[basicAnim];
groupAnim.duration = [self maxDurationFromAnimations:groupAnim.animations];
[groupAnim.animations setValue:kCAFillModeBoth forKeyPath:@"fillMode"];
newAnim = groupAnim;
}else newAnim = basicAnim;
}
else if ([anim isKindOfClass:[CAKeyframeAnimation class]]) {
CAKeyframeAnimation *keyAnim = (CAKeyframeAnimation*)anim;
if (!anim.autoreverses) {
NSArray *values = [keyAnim.values reverseObjectEnumerator].allObjects;
keyAnim.values = values;
reverseTimingFunction(keyAnim);
}
keyAnim.beginTime = reverseStartTime;
if (reverseStartTime > 0) {
CAAnimationGroup *groupAnim = [CAAnimationGroup animation];
groupAnim.animations = @[keyAnim];
groupAnim.duration = [self maxDurationFromAnimations:groupAnim.animations];
[groupAnim.animations setValue:kCAFillModeBoth forKeyPath:@"fillMode"];
newAnim = groupAnim;
}else newAnim = keyAnim;
}
else if ([anim isKindOfClass:[CAAnimationGroup class]]) {
CAAnimationGroup *groupAnim = (CAAnimationGroup*)anim;
NSMutableArray *newSubAnims = [NSMutableArray arrayWithCapacity:groupAnim.animations.count];
for (CAAnimation *subAnim in groupAnim.animations) {
CAAnimation *newSubAnim = [self reverseAnimation:subAnim totalDuration:totalDuration];
[newSubAnims addObject:newSubAnim];
}
groupAnim.animations = newSubAnims;
[groupAnim.animations setValue:kCAFillModeBoth forKeyPath:@"fillMode"];
groupAnim.duration = [self maxDurationFromAnimations:newSubAnims];
newAnim = groupAnim;
}else newAnim = anim;
return newAnim;
}
+ (CAAnimationGroup*)groupAnimations:(NSArray*)animations fillMode:(NSString*)fillMode forEffectLayer:(BOOL)forEffectLayer sublayersCount:(NSInteger)count{
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = animations;
if (fillMode) {
[groupAnimation.animations setValue:fillMode forKeyPath:@"fillMode"];
groupAnimation.fillMode = fillMode;
groupAnimation.removedOnCompletion = NO;
}
if (forEffectLayer) {
groupAnimation.duration = [QCMethod maxDurationOfEffectAnimation:groupAnimation sublayersCount:count];
}else{
groupAnimation.duration = [QCMethod maxDurationFromAnimations:animations];
}
return groupAnimation;
}
+ (CAAnimationGroup*)groupAnimations:(NSArray*)animations fillMode:(NSString*)fillMode{
return [self groupAnimations:animations fillMode:fillMode forEffectLayer:NO sublayersCount:0];
}
+ (CGFloat)maxDurationFromAnimations:(NSArray*)anims{
CGFloat maxDuration = 0;
for (CAAnimation *anim in anims) {
maxDuration = MAX(anim.beginTime + anim.duration * (CGFloat)(anim.repeatCount == 0 ? 1.0f : anim.repeatCount) * (anim.autoreverses ? 2.0f : 1.0f), maxDuration);
}
if (maxDuration == INFINITY) {
maxDuration = 1000.0f;
}
return maxDuration;
}
+ (CGFloat)maxDurationOfEffectAnimation:(CAAnimationGroup*)anim sublayersCount:(NSInteger)count{
CGFloat maxDuration = 0;
if ([anim isKindOfClass:[CAAnimationGroup class]]) {
for (CABasicAnimation *subAnim in anim.animations) {
CGFloat instanceDelay = [[subAnim valueForKey:@"instanceDelay"] floatValue];
CGFloat delay = instanceDelay * (CGFloat)(count - 1);
CGFloat repeatCountDuration = 0;
if (subAnim.repeatCount >1) {
repeatCountDuration = (subAnim.duration * (subAnim.repeatCount-1));
}
CGFloat duration = subAnim.beginTime + (subAnim.autoreverses ? subAnim.duration : 0) + (delay + subAnim.duration + repeatCountDuration);
maxDuration = MAX(duration, maxDuration);
}
}
if (maxDuration == INFINITY) {
maxDuration = 1000.0f;
}
return maxDuration;
}
+ (void)updateValueFromAnimationsForLayers:(NSArray*)layers{
[CATransaction begin];
[CATransaction setDisableActions:YES];
for (CALayer *layer in layers) {
for (NSString *animKey in layer.animationKeys) {
CAAnimation *anim = [layer animationForKey:animKey];
[self updateValueForAnimation:anim theLayer:layer];
}
}
[CATransaction commit];
}
+ (void)updateValueForAnimation:(CAAnimation*)anim theLayer:(CALayer *)layer{
if ([anim isKindOfClass:[CABasicAnimation class]]) {
CABasicAnimation *basicAnim = (CABasicAnimation*)anim;
if (!basicAnim.autoreverses) {
[layer setValue:basicAnim.toValue forKeyPath:basicAnim.keyPath];
}
}
else if ([anim isKindOfClass:[CAKeyframeAnimation class]]) {
CAKeyframeAnimation *keyAnim = (CAKeyframeAnimation*)anim;
if (!anim.autoreverses) {
[layer setValue:keyAnim.values.lastObject forKeyPath:keyAnim.keyPath];
}
}
else if ([anim isKindOfClass:[CAAnimationGroup class]]) {
CAAnimationGroup *groupAnim = (CAAnimationGroup*)anim;
for (CAAnimation *subAnim in groupAnim.animations) {
[self updateValueForAnimation:subAnim theLayer:layer];
}
}
}
+ (void)updateValueFromPresentationLayerForAnimation:(CAAnimation*)anim theLayer:(CALayer *)layer{
if ([anim isKindOfClass:[CABasicAnimation class]] || [anim isKindOfClass:[CAKeyframeAnimation class]]) {
CABasicAnimation *basicAnim = (CABasicAnimation*)anim;
[layer setValue:[layer.presentationLayer valueForKeyPath:basicAnim.keyPath] forKeyPath:basicAnim.keyPath];
}
else if ([anim isKindOfClass:[CAAnimationGroup class]]) {
CAAnimationGroup *groupAnim = (CAAnimationGroup*)anim;
for (CAAnimation *subAnim in groupAnim.animations) {
[self updateValueFromPresentationLayerForAnimation:subAnim theLayer:layer];
}
}
}
+ (void)addSublayersAnimation:(CAAnimation*)anim forKey:(NSString*)key forLayer:(CALayer*)layer{
[self addSublayersAnimationNeedReverse:anim forKey:key forLayer:layer reverseAnimation:NO totalDuration:0];
}
//!Add animation to each sublayer in effect layer
+ (void)addSublayersAnimationNeedReverse:(CAAnimation*)anim forKey:(NSString*)key forLayer:(CALayer*)layer reverseAnimation:(BOOL)reverse totalDuration:(CGFloat)totalDuration{
NSArray *sublayers = layer.sublayers;
NSInteger sublayersCount = sublayers.count;
void (^setBeginTime)(CAAnimation*, NSInteger) = ^(CAAnimation *subAnim, NSInteger sublayerIdx){
CGFloat instanceDelay = [[subAnim valueForKey:@"instanceDelay"] floatValue];
NSInteger orderType = [[subAnim valueForKey:@"instanceOrder"] integerValue];
switch (orderType) {
case 0: subAnim.beginTime += sublayerIdx * instanceDelay; break;
case 1: subAnim.beginTime += (sublayersCount - sublayerIdx - 1) * instanceDelay; break;
case 2: {
CGFloat middleIdx = sublayersCount/2.0f;
CGFloat begin = fabs((middleIdx - sublayerIdx)) * instanceDelay ;
subAnim.beginTime += begin; break;
}
case 3: {
CGFloat middleIdx = sublayersCount/2.0f;
CGFloat begin = (middleIdx - fabs((middleIdx - sublayerIdx))) * instanceDelay ;
subAnim.beginTime += begin; break;
}
case 4: {
//Add yours here
}
default:
break;
}
};
[sublayers enumerateObjectsWithOptions:0 usingBlock:^(CALayer *sublayer, NSUInteger idx, BOOL *stop) {
if ([anim isKindOfClass:[CAAnimationGroup class]]) {
CAAnimationGroup *groupAnim = (CAAnimationGroup*)anim.copy;
NSMutableArray *newSubAnimations = [NSMutableArray arrayWithCapacity:groupAnim.animations.count];
for (CABasicAnimation *subAnim in groupAnim.animations) {
[newSubAnimations addObject:subAnim.copy];
}
groupAnim.animations = newSubAnimations;
NSArray *animations = ((CAAnimationGroup*)groupAnim).animations;
for (CABasicAnimation *sub in animations) {
setBeginTime(sub, idx);
//Reverse animation if needed
if (reverse) [self reverseAnimation:sub totalDuration:totalDuration];
}
[sublayer addAnimation:groupAnim forKey:key];
}
else{
CABasicAnimation *copiedAnim = anim.copy;
setBeginTime(copiedAnim, idx);
[sublayer addAnimation:copiedAnim forKey:key];
}
}];
}
#if (TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE)
+ (UIBezierPath*)alignToBottomPath:(UIBezierPath*)path layer:(CALayer*)layer{
CGFloat diff = CGRectGetMaxY(layer.bounds) - CGRectGetMaxY(path.bounds);
CGAffineTransform affineTransform = CGAffineTransformTranslate(CGAffineTransformIdentity, 0, diff);
[path applyTransform:affineTransform];
return path;
}
+ (UIBezierPath*)offsetPath:(UIBezierPath*)path by:(CGPoint)offset{
CGAffineTransform affineTransform = CGAffineTransformTranslate(CGAffineTransformIdentity, offset.x, offset.y);
[path applyTransform:affineTransform];
return path;
}
#else
+ (NSBezierPath*)offsetPath:(NSBezierPath*)path by:(CGPoint)offset{
NSAffineTransform* xfm = [NSAffineTransform transform];
[xfm translateXBy:offset.x yBy:offset.y];
[path transformUsingAffineTransform:xfm];
return path;
}
#endif
@end
#if (TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE)
#else
@implementation NSBezierPath (Path)
- (CGPathRef)quartzPath
{
NSInteger i, numElements;
CGPathRef immutablePath = NULL;
numElements = self.elementCount;
if (numElements > 0)
{
CGMutablePathRef path = CGPathCreateMutable();
NSPoint points[3];
BOOL didClosePath = YES;
for (i = 0; i < numElements; i++)
{
switch ([self elementAtIndex:i associatedPoints:points])
{
case NSMoveToBezierPathElement:
CGPathMoveToPoint(path, NULL, points[0].x, points[0].y);
break;
case NSLineToBezierPathElement:
CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y);
didClosePath = NO;
break;
case NSCurveToBezierPathElement:
CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y,
points[1].x, points[1].y,
points[2].x, points[2].y);
didClosePath = NO;
break;
case NSClosePathBezierPathElement:
CGPathCloseSubpath(path);
didClosePath = YES;
break;
}
}
if (!didClosePath){
//CGPathCloseSubpath(path);
}
immutablePath = CGPathCreateCopy(path);
CGPathRelease(path);
}
return immutablePath;
}
@end
@implementation NSImage (cgImage)
-(CGImageRef)cgImage{
NSData* data = self.TIFFRepresentation;
CGImageRef imageRef = NULL;
CGImageSourceRef sourceRef;
sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
if(sourceRef) {
imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
CFRelease(sourceRef);
}
return imageRef;
}
@end
#endif