910e62b5创建于 1月15日历史提交
// Copyright 2025 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/composebox/ui/composebox_dismiss_animator.h"

#import "ios/chrome/browser/composebox/ui/composebox_animation_context.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"

@implementation ComposeboxDismissAnimator {
  __weak id<ComposeboxAnimationContext> _contextProvider;
  __weak id<ComposeboxAnimationBase> _animationBase;
}

- (instancetype)
    initWithContextProvider:(id<ComposeboxAnimationContext>)contextProvider
              animationBase:(id<ComposeboxAnimationBase>)animationBase {
  self = [super init];
  if (self) {
    _contextProvider = contextProvider;
    _animationBase = animationBase;
  }
  return self;
}

- (NSTimeInterval)transitionDuration:
    (id<UIViewControllerContextTransitioning>)transitionContext {
  if (UIAccessibilityIsReduceMotionEnabled()) {
    return 0.2;
  }
  return 0.3;
}

- (void)animateTransition:
    (id<UIViewControllerContextTransitioning>)transitionContext {
  UIView* entrypointCopy = [_animationBase entrypointViewVisualCopy];
  UIView* fromView =
      [transitionContext viewForKey:UITransitionContextFromViewKey];

  BOOL isLandscape = IsLandscape(fromView.window);
  BOOL compact = [_contextProvider inputPlateIsCompact];
  BOOL reduceMotion = UIAccessibilityIsReduceMotionEnabled();
  if (reduceMotion) {
    [self animateTransitionWithReducedMotion:transitionContext];
    return;
  }
  // If the final state is too far visually from the current input plate state,
  // use the simplified animation.
  if (!entrypointCopy || isLandscape || !compact) {
    [self animateTransitionWithoutSharedElement:transitionContext];
    return;
  }

  UIView* transitionContainerView = [transitionContext containerView];
  UIView* inputPlateView = [_contextProvider inputPlateViewForAnimation];
  UIView* closeButton = [_contextProvider closeButtonForAnimation];
  UIView* popupView = [_contextProvider popupViewForAnimation];
  [transitionContainerView addSubview:entrypointCopy];
  [entrypointCopy layoutIfNeeded];
  entrypointCopy.alpha = 0;

  [_contextProvider expandInputPlateForDismissal];
  [UIView
      animateKeyframesWithDuration:[self transitionDuration:transitionContext]
      delay:0
      options:UIViewAnimationCurveEaseInOut
      animations:^{
        // Morphing the input plate.
        [UIView addKeyframeWithRelativeStartTime:0
                                relativeDuration:0.5
                                      animations:^{
                                        [inputPlateView.superview
                                                .superview layoutIfNeeded];
                                      }];
        [UIView addKeyframeWithRelativeStartTime:0.2
                                relativeDuration:0.6
                                      animations:^{
                                        inputPlateView.transform =
                                            CGAffineTransformMakeScale(1, 0.9);
                                      }];
        [UIView
            addKeyframeWithRelativeStartTime:0.3
                            relativeDuration:0.6
                                  animations:^{
                                    inputPlateView.alpha = 0;
                                    closeButton.alpha = 0;
                                    closeButton.transform =
                                        CGAffineTransformMakeScale(0.9, 0.9);
                                  }];
        // Briefly show the copy then fade the entire composebox view.
        [UIView addKeyframeWithRelativeStartTime:0.5
                                relativeDuration:0.3
                                      animations:^{
                                        entrypointCopy.alpha = 1;
                                      }];
        [UIView addKeyframeWithRelativeStartTime:0
                                relativeDuration:0.8
                                      animations:^{
                                        popupView.alpha = 0.2;
                                      }];
        [UIView addKeyframeWithRelativeStartTime:0.8
                                relativeDuration:0.2
                                      animations:^{
                                        fromView.alpha = 0;
                                        entrypointCopy.alpha = 0;
                                      }];
      }
      completion:^(BOOL finished) {
        [transitionContext completeTransition:finished];
      }];
}

- (void)animateTransitionWithoutSharedElement:
    (id<UIViewControllerContextTransitioning>)transitionContext {
  UIView* fromView =
      [transitionContext viewForKey:UITransitionContextFromViewKey];
  UIView* inputPlateView = [_contextProvider inputPlateViewForAnimation];
  UIView* closeButton = [_contextProvider closeButtonForAnimation];
  BOOL isLandscape = IsLandscape(fromView.window);

  CGFloat scaleAmmount = isLandscape ? 0.7 : 0.9;
  [UIView
      animateKeyframesWithDuration:[self transitionDuration:transitionContext]
      delay:0
      options:UIViewAnimationCurveEaseInOut
      animations:^{
        // Morphing the input plate.
        [UIView addKeyframeWithRelativeStartTime:0
                                relativeDuration:1
                                      animations:^{
                                        fromView.alpha = 0;
                                      }];
        [UIView addKeyframeWithRelativeStartTime:0.2
                                relativeDuration:0.6
                                      animations:^{
                                        inputPlateView.transform =
                                            CGAffineTransformMakeScale(
                                                scaleAmmount, scaleAmmount);
                                        closeButton.transform =
                                            CGAffineTransformMakeScale(
                                                scaleAmmount, scaleAmmount);
                                      }];
      }
      completion:^(BOOL finished) {
        [transitionContext completeTransition:finished];
      }];
}

- (void)animateTransitionWithReducedMotion:
    (id<UIViewControllerContextTransitioning>)transitionContext {
  UIView* fromView =
      [transitionContext viewForKey:UITransitionContextFromViewKey];

  [transitionContext.containerView addSubview:fromView];
  [UIView animateWithDuration:[self transitionDuration:transitionContext]
      delay:0
      options:UIViewAnimationCurveLinear
      animations:^{
        fromView.alpha = 0;
      }
      completion:^(BOOL finished) {
        [transitionContext completeTransition:finished];
      }];
}

@end