chromium/ios/chrome/browser/ui/scanner/scanner_transitioning_delegate.mm

// Copyright 2019 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/scanner/scanner_transitioning_delegate.h"

#import "base/ios/block_types.h"

namespace {

// The default animation duration.
const NSTimeInterval kDefaultDuration = 0.25;

enum ScannerTransition { PRESENT, DISMISS };

}  // namespace

// Animates the Scanner transition. If initialized with the `PRESENT`
// transition, positions the Scanner view below its presenting view
// controller's view in the container view and animates the presenting view to
// slide up. If initialized with the `DISMISS` transition, positions the
// presenting view controller's view above the Scanner view in the container
// view and animates the presenting view to slide down.
@interface ScannerTransitionAnimator
    : NSObject <UIViewControllerAnimatedTransitioning> {
  ScannerTransition _transition;
}

- (instancetype)initWithTransition:(ScannerTransition)transition;

@end

@implementation ScannerTransitionAnimator

- (instancetype)initWithTransition:(ScannerTransition)transition {
  self = [super init];
  if (self) {
    _transition = transition;
  }
  return self;
}

- (void)animateTransition:
    (id<UIViewControllerContextTransitioning>)transitionContext {
  UIView* containerView = [transitionContext containerView];
  UIView* presentedView =
      [transitionContext viewForKey:UITransitionContextToViewKey];
  UIView* presentingView =
      [transitionContext viewForKey:UITransitionContextFromViewKey];

  // Get the final frame for the presented view.
  UIViewController* presentedViewController = [transitionContext
      viewControllerForKey:UITransitionContextToViewControllerKey];
  CGRect finalFrame =
      [transitionContext finalFrameForViewController:presentedViewController];

  ProceduralBlock animations;

  // Set the frame for the presented view.
  presentedView.frame = finalFrame;
  [presentedView layoutIfNeeded];

  switch (_transition) {
    case PRESENT: {
      [containerView insertSubview:presentedView belowSubview:presentingView];
      animations = ^void {
        presentingView.transform =
            CGAffineTransformMakeTranslation(0, -finalFrame.size.height);
      };
      break;
    }
    case DISMISS: {
      [containerView insertSubview:presentedView aboveSubview:presentingView];
      presentedView.transform =
          CGAffineTransformMakeTranslation(0, -finalFrame.size.height);
      animations = ^void {
        presentedView.transform = CGAffineTransformIdentity;
      };
      break;
    }
  }

  void (^completion)(BOOL) = ^void(BOOL finished) {
    presentingView.transform = CGAffineTransformIdentity;
    [transitionContext completeTransition:finished];
  };

  // Animate the transition.
  [UIView animateWithDuration:kDefaultDuration
                        delay:0
                      options:UIViewAnimationOptionCurveEaseInOut
                   animations:animations
                   completion:completion];
}

- (NSTimeInterval)transitionDuration:
    (id<UIViewControllerContextTransitioning>)transitionContext {
  return kDefaultDuration;
}

@end

@implementation ScannerTransitioningDelegate

- (id<UIViewControllerAnimatedTransitioning>)
    animationControllerForPresentedController:(UIViewController*)presented
                         presentingController:(UIViewController*)presenting
                             sourceController:(UIViewController*)source {
  return [[ScannerTransitionAnimator alloc] initWithTransition:PRESENT];
}

- (id<UIViewControllerAnimatedTransitioning>)
    animationControllerForDismissedController:(UIViewController*)dismissed {
  return [[ScannerTransitionAnimator alloc] initWithTransition:DISMISS];
}

@end