chromium/ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/animations/centered_zoom_transition_animation.mm

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/ui/tab_switcher/tab_grid/transitions/animations/centered_zoom_transition_animation.h"

#import "base/ios/block_types.h"
#import "ios/chrome/browser/shared/ui/util/named_guide.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"

namespace {

// Animation constants.
const CGFloat kAnimationDuration = 0.25;
const CGFloat kFinalAlpha = 1.0;
const CGFloat kInitialAlpha = 0.0;
const CGFloat kInitialCornerRadius = 26.0;
const CGFloat kInitialTransformScale = 0.75;

// Structure to hold animation parameters.
typedef struct {
  CGFloat alpha;
  CGFloat cornerRadius;
  BOOL clipsToBounds;
  CGAffineTransform transform;
} AnimationParameters;

// Creates animation parameters for the contracted animation.
AnimationParameters CreateContractedAnimationParametersForView(UIView* view) {
  return {kInitialAlpha, kInitialCornerRadius, YES,
          CGAffineTransformScale(view.transform, kInitialTransformScale,
                                 kInitialTransformScale)};
}

// Creates animation parameters for the expanded animation.
AnimationParameters CreateExpandedAnimationParametersForView(UIView* view) {
  return {kFinalAlpha, DeviceCornerRadius(), YES, view.transform};
}

}  // namespace

@implementation CenteredZoomTransitionAnimation {
  UIView* _animatedView;
  CenteredZoomTransitionAnimationDirection _direction;

  AnimationParameters _initialAnimationParameters;
  AnimationParameters _finalAnimationParameters;
}

#pragma mark - Public

- (instancetype)initWithView:(UIView*)view
                   direction:
                       (CenteredZoomTransitionAnimationDirection)direction {
  self = [super init];
  if (self) {
    _animatedView = view;
    _direction = direction;
  }
  return self;
}

#pragma mark - TabGridTransitionAnimation

- (void)animateWithCompletion:(ProceduralBlock)completion {
  [self setupAnimationParameters];
  [self setInitialAnimationParameters];

  __weak __typeof(self) weakSelf = self;
  ProceduralBlock animationsBlock = ^{
    [weakSelf performAnimation];
  };

  void (^completionBlock)(BOOL) = ^(BOOL finished) {
    // When presenting the FirstRun ViewController, this can be called with
    // `finished` to NO on official builds. For now, the animation not
    // finishing isn't handled anywhere.
    if (weakSelf) {
      [weakSelf performAnimationCompletion:completion];
      return;
    }
    if (completion) {
      completion();
    }
  };

  [UIView animateWithDuration:kAnimationDuration
                        delay:0.0
                      options:UIViewAnimationOptionCurveEaseOut
                   animations:animationsBlock
                   completion:completionBlock];
}

#pragma mark - Private

// Performs the animation for the `animateWithCompletion` method.
- (void)performAnimation {
  _animatedView.alpha = _finalAnimationParameters.alpha;
  _animatedView.transform = _finalAnimationParameters.transform;
  _animatedView.layer.cornerRadius = _finalAnimationParameters.cornerRadius;
}

// Performs the animation completion block for the `animateWithCompletion:`
// method.
- (void)performAnimationCompletion:(ProceduralBlock)completion {
  _animatedView.clipsToBounds = _finalAnimationParameters.clipsToBounds;

  if (completion) {
    completion();
  }
}

// Creates animation parameters.
- (void)setupAnimationParameters {
  switch (_direction) {
    case CenteredZoomTransitionAnimationDirection::kContracting:
      _initialAnimationParameters =
          CreateExpandedAnimationParametersForView(_animatedView);
      _finalAnimationParameters =
          CreateContractedAnimationParametersForView(_animatedView);
      break;
    case CenteredZoomTransitionAnimationDirection::kExpanding:
      _initialAnimationParameters =
          CreateContractedAnimationParametersForView(_animatedView);
      _finalAnimationParameters =
          CreateExpandedAnimationParametersForView(_animatedView);
      break;
  }

  _initialAnimationParameters.clipsToBounds = YES;
  _finalAnimationParameters.clipsToBounds = _animatedView.clipsToBounds;
}

// Sets initial animation parameters to the view.
- (void)setInitialAnimationParameters {
  _animatedView.alpha = _initialAnimationParameters.alpha;
  _animatedView.transform = _initialAnimationParameters.transform;
  _animatedView.layer.cornerRadius = _initialAnimationParameters.cornerRadius;
  _animatedView.clipsToBounds = _initialAnimationParameters.clipsToBounds;
}

@end