|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ios/chrome/browser/ui/animation_util.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cmath> | 
|  |  | 
|  | #include "ios/chrome/browser/ui/reversed_animation.h" | 
|  |  | 
|  | #if !defined(__has_feature) || !__has_feature(objc_arc) | 
|  | #error "This file requires ARC support." | 
|  | #endif | 
|  |  | 
|  | CAAnimation* FrameAnimationMake(CALayer* layer, | 
|  | CGRect beginFrame, | 
|  | CGRect endFrame) { | 
|  | CGRect beginBounds = {CGPointZero, beginFrame.size}; | 
|  | CGRect endBounds = {CGPointZero, endFrame.size}; | 
|  | CABasicAnimation* boundsAnimation = | 
|  | [CABasicAnimation animationWithKeyPath:@"bounds"]; | 
|  | boundsAnimation.fromValue = [NSValue valueWithCGRect:beginBounds]; | 
|  | boundsAnimation.toValue = [NSValue valueWithCGRect:endBounds]; | 
|  | boundsAnimation.removedOnCompletion = NO; | 
|  | boundsAnimation.fillMode = kCAFillModeBoth; | 
|  | CGPoint beginPosition = CGPointMake( | 
|  | beginFrame.origin.x + layer.anchorPoint.x * beginBounds.size.width, | 
|  | beginFrame.origin.y + layer.anchorPoint.y * beginBounds.size.height); | 
|  | CGPoint endPosition = CGPointMake( | 
|  | endFrame.origin.x + layer.anchorPoint.x * endBounds.size.width, | 
|  | endFrame.origin.y + layer.anchorPoint.y * endBounds.size.height); | 
|  | CABasicAnimation* positionAnimation = | 
|  | [CABasicAnimation animationWithKeyPath:@"position"]; | 
|  | positionAnimation.fromValue = [NSValue valueWithCGPoint:beginPosition]; | 
|  | positionAnimation.toValue = [NSValue valueWithCGPoint:endPosition]; | 
|  | positionAnimation.removedOnCompletion = NO; | 
|  | positionAnimation.fillMode = kCAFillModeBoth; | 
|  | return AnimationGroupMake(@[ boundsAnimation, positionAnimation ]); | 
|  | } | 
|  |  | 
|  | CAAnimation* OpacityAnimationMake(CGFloat beginOpacity, CGFloat endOpacity) { | 
|  | CABasicAnimation* opacityAnimation = | 
|  | [CABasicAnimation animationWithKeyPath:@"opacity"]; | 
|  | opacityAnimation.fromValue = @(beginOpacity); | 
|  | opacityAnimation.toValue = @(endOpacity); | 
|  | opacityAnimation.fillMode = kCAFillModeBoth; | 
|  | opacityAnimation.removedOnCompletion = NO; | 
|  | return opacityAnimation; | 
|  | } | 
|  |  | 
|  | CAAnimation* AnimationGroupMake(NSArray* animations) { | 
|  | CAAnimationGroup* animationGroup = [CAAnimationGroup animation]; | 
|  | animationGroup.animations = animations; | 
|  | CFTimeInterval duration = 0.0; | 
|  | for (CAAnimation* animation in animations) | 
|  | duration = std::max(duration, animation.beginTime + animation.duration); | 
|  | animationGroup.duration = duration; | 
|  | animationGroup.fillMode = kCAFillModeBoth; | 
|  | animationGroup.removedOnCompletion = NO; | 
|  | return animationGroup; | 
|  | } | 
|  |  | 
|  | CAAnimation* DelayedAnimationMake(CAAnimation* animation, | 
|  | CFTimeInterval delay) { | 
|  | CAAnimation* delayedAnimation = [animation copy]; | 
|  | if (delayedAnimation) { | 
|  | delayedAnimation.beginTime = delay; | 
|  | delayedAnimation = AnimationGroupMake(@[ delayedAnimation ]); | 
|  | } | 
|  | return delayedAnimation; | 
|  | } | 
|  |  | 
|  | CABasicAnimation* FindAnimationForKeyPath(NSString* keyPath, | 
|  | CAAnimation* animation) { | 
|  | __block CABasicAnimation* animationForKeyPath = nil; | 
|  | if ([animation isKindOfClass:[CABasicAnimation class]]) { | 
|  | CABasicAnimation* basicAnimation = | 
|  | static_cast<CABasicAnimation*>(animation); | 
|  | if ([basicAnimation.keyPath isEqualToString:keyPath]) | 
|  | animationForKeyPath = basicAnimation; | 
|  | } else if ([animation isKindOfClass:[CAAnimationGroup class]]) { | 
|  | CAAnimationGroup* animationGroup = | 
|  | static_cast<CAAnimationGroup*>(animation); | 
|  | [animationGroup.animations | 
|  | enumerateObjectsUsingBlock:^(CAAnimation* subAnimation, NSUInteger idx, | 
|  | BOOL* stop) { | 
|  | animationForKeyPath = FindAnimationForKeyPath(keyPath, subAnimation); | 
|  | *stop = animationForKeyPath != nil; | 
|  | }]; | 
|  | } | 
|  | return animationForKeyPath; | 
|  | } | 
|  |  | 
|  | void RemoveAnimationForKeyFromLayers(NSString* key, NSArray* layers) { | 
|  | for (CALayer* layer in layers) | 
|  | [layer removeAnimationForKey:key]; | 
|  | } |