Commit 408434fc authored by Rohit Rao's avatar Rohit Rao Committed by Commit Bot

[ios] Adds animations for Recent Tabs presentation and dismissal.

Also adds a shadow to the presentation controller to visually separate the
recent tabs content from the background views.

BUG=805154

Cq-Include-Trybots: master.tryserver.chromium.mac:ios-simulator-cronet;master.tryserver.chromium.mac:ios-simulator-full-configs
Change-Id: Ifde4a44c0eac0157ca02fc93c7814a78b51cca12
Reviewed-on: https://chromium-review.googlesource.com/998416
Commit-Queue: Rohit Rao <rohitrao@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#549554}
parent b88fb10d
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#import "ios/chrome/browser/ui/recent_tabs/recent_tabs_transitioning_delegate.h" #import "ios/chrome/browser/ui/recent_tabs/recent_tabs_transitioning_delegate.h"
#import "ios/chrome/browser/ui/table_view/table_view_animator.h"
#import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h" #import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
...@@ -21,4 +22,34 @@ presentationControllerForPresentedViewController:(UIViewController*)presented ...@@ -21,4 +22,34 @@ presentationControllerForPresentedViewController:(UIViewController*)presented
presentingViewController:presenting]; presentingViewController:presenting];
} }
- (id<UIViewControllerAnimatedTransitioning>)
animationControllerForPresentedController:(UIViewController*)presented
presentingController:(UIViewController*)presenting
sourceController:(UIViewController*)source {
UITraitCollection* traitCollection = presenting.traitCollection;
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
// Use the default animator for fullscreen presentations.
return nil;
}
TableViewAnimator* animator = [[TableViewAnimator alloc] init];
animator.presenting = YES;
return animator;
}
- (id<UIViewControllerAnimatedTransitioning>)
animationControllerForDismissedController:(UIViewController*)dismissed {
UITraitCollection* traitCollection = dismissed.traitCollection;
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
// Use the default animator for fullscreen presentations.
return nil;
}
TableViewAnimator* animator = [[TableViewAnimator alloc] init];
animator.presenting = NO;
return animator;
}
@end @end
...@@ -39,10 +39,15 @@ source_set("styler") { ...@@ -39,10 +39,15 @@ source_set("styler") {
source_set("presentation") { source_set("presentation") {
configs += [ "//build/config/compiler:enable_arc" ] configs += [ "//build/config/compiler:enable_arc" ]
sources = [ sources = [
"table_view_animator.h",
"table_view_animator.mm",
"table_view_presentation_controller.h", "table_view_presentation_controller.h",
"table_view_presentation_controller.mm", "table_view_presentation_controller.mm",
] ]
deps = [ deps = [
"//ios/chrome/browser/ui:ui_util",
"//ios/chrome/browser/ui/image_util",
"//ios/chrome/browser/ui/resources:menu_shadow",
"//ios/chrome/browser/ui/util:constraints_ui", "//ios/chrome/browser/ui/util:constraints_ui",
] ]
} }
......
// Copyright 2018 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.
#ifndef IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_ANIMATOR_H_
#define IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_ANIMATOR_H_
#import <UIKit/UIKit.h>
// TableViewAnimator implements an animation that slides the presented view in
// from the trailing edge of the screen.
@interface TableViewAnimator : NSObject<UIViewControllerAnimatedTransitioning>
// YES if this animator is presenting a view controller, NO if it is dismissing
// one.
@property(nonatomic, assign) BOOL presenting;
@end
#endif // IOS_CHROME_BROWSER_UI_TABLE_VIEW_TABLE_VIEW_ANIMATOR_H_
// Copyright 2018 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.
#import "ios/chrome/browser/ui/table_view/table_view_animator.h"
#import "ios/chrome/browser/ui/rtl_geometry.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation TableViewAnimator
@synthesize presenting = _presenting;
- (NSTimeInterval)transitionDuration:
(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.25;
}
- (void)animateTransition:
(id<UIViewControllerContextTransitioning>)transitionContext {
// Set up the keys for the "base" view/VC and the "presented" view/VC. These
// will be used to fetch the associated objects later.
NSString* baseViewKey = self.presenting ? UITransitionContextFromViewKey
: UITransitionContextToViewKey;
NSString* presentedViewControllerKey =
self.presenting ? UITransitionContextToViewControllerKey
: UITransitionContextFromViewControllerKey;
NSString* presentedViewKey = self.presenting ? UITransitionContextToViewKey
: UITransitionContextFromViewKey;
// Get views and view controllers for this transition.
UIView* baseView = [transitionContext viewForKey:baseViewKey];
UIViewController* presentedViewController =
[transitionContext viewControllerForKey:presentedViewControllerKey];
UIView* presentedView = [transitionContext viewForKey:presentedViewKey];
// Always add the destination view to the container.
UIView* containerView = [transitionContext containerView];
if (self.presenting) {
[containerView addSubview:presentedView];
} else {
[containerView addSubview:baseView];
}
// Set the initial frame and Compute the final frame for the presented view.
LayoutRect presentedViewFinalFrame = LayoutRectZero;
if (self.presenting) {
presentedViewFinalFrame = LayoutRectForRectInBoundingRect(
[transitionContext finalFrameForViewController:presentedViewController],
containerView.bounds);
LayoutRect presentedViewStartFrame = presentedViewFinalFrame;
presentedViewStartFrame.position.leading =
CGRectGetWidth(containerView.bounds);
presentedView.frame = LayoutRectGetRect(presentedViewStartFrame);
} else {
presentedViewFinalFrame = LayoutRectForRectInBoundingRect(
presentedView.frame, containerView.bounds);
presentedViewFinalFrame.position.leading =
CGRectGetWidth(containerView.bounds);
}
// Animate using the animator's own duration value.
[UIView animateWithDuration:[self transitionDuration:transitionContext]
delay:0
usingSpringWithDamping:0.85
initialSpringVelocity:0
options:UIViewAnimationOptionTransitionNone
animations:^{
presentedView.frame = LayoutRectGetRect(presentedViewFinalFrame);
}
completion:^(BOOL finished) {
BOOL success = ![transitionContext transitionWasCancelled];
// If presentation failed, remove the view.
if (self.presenting && !success) {
[presentedView removeFromSuperview];
}
// Notify UIKit that the transition has finished
[transitionContext completeTransition:success];
}];
}
@end
...@@ -4,8 +4,13 @@ ...@@ -4,8 +4,13 @@
#import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h" #import "ios/chrome/browser/ui/table_view/table_view_presentation_controller.h"
#include <algorithm>
#import "ios/chrome/browser/ui/image_util/image_util.h"
#import "ios/chrome/browser/ui/util/constraints_ui_util.h" #import "ios/chrome/browser/ui/util/constraints_ui_util.h"
#include "ios/chrome/browser/ui/rtl_geometry.h"
#if !defined(__has_feature) || !__has_feature(objc_arc) #if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support." #error "This file requires ARC support."
#endif #endif
...@@ -13,65 +18,144 @@ ...@@ -13,65 +18,144 @@
namespace { namespace {
// The rounded corner radius for the bubble container, in Regular widths. // The rounded corner radius for the bubble container, in Regular widths.
const CGFloat kContainerCornerRadius = 13.0; const CGFloat kContainerCornerRadius = 13.0;
// The size of the margin around |tableViewContainer| in which the shadow is
// drawn.
const CGFloat kShadowMargin = 196.0;
// The minimum margin between |tableViewContainer| and any screen edge.
const CGFloat kTableViewEdgeMargin = 15.0;
// The top margin for |tableViewContainer|.
const CGFloat kTableViewTopMargin = 35.0;
// The maximum allowed width for |tableViewContainer|.
const CGFloat kTableViewMaxWidth = 414.0;
} }
@interface TableViewPresentationController () @interface TableViewPresentationController ()
@property(nonatomic, readwrite, strong) UIVisualEffectView* tableViewContainer; @property(nonatomic, readwrite, strong) UIView* shadowContainer;
@property(nonatomic, readwrite, strong) UIImageView* shadowImage;
@property(nonatomic, readwrite, strong) UIView* tableViewContainer;
// Cleans up and removes any views that are managed by this controller.
- (void)cleanUpPresentationContainerViews;
@end @end
@implementation TableViewPresentationController @implementation TableViewPresentationController
@synthesize shadowContainer = _shadowContainer;
@synthesize shadowImage = _shadowImage;
@synthesize tableViewContainer = _tableViewContainer; @synthesize tableViewContainer = _tableViewContainer;
- (CGRect)frameOfPresentedViewInContainerView { - (CGRect)frameOfPresentedViewInContainerView {
// TODO(crbug.com/820154): The constants below are arbitrary and need to be // The tableview container should be pinned to the top, bottom, and trailing
// replaced with final values. // edges of the screen, with a fixed margin on those sides.
return UIEdgeInsetsInsetRect(self.containerView.frame, CGFloat containerWidth = CGRectGetWidth(self.containerView.bounds);
UIEdgeInsetsMake(90, 200, 60, 10)); CGFloat containerHeight = CGRectGetHeight(self.containerView.bounds);
CGFloat maxAvailableWidth = containerWidth - 2 * kTableViewEdgeMargin;
CGFloat tableWidth = std::min(maxAvailableWidth, kTableViewMaxWidth);
LayoutRect tableLayoutRect = LayoutRectMake(
containerWidth - tableWidth - kTableViewEdgeMargin, containerWidth,
kTableViewTopMargin, tableWidth,
containerHeight - kTableViewTopMargin - kTableViewEdgeMargin);
return LayoutRectGetRect(tableLayoutRect);
} }
- (UIView*)presentedView { - (UIView*)presentedView {
return self.tableViewContainer; return self.shadowContainer;
} }
- (void)presentationTransitionWillBegin { - (void)presentationTransitionWillBegin {
self.tableViewContainer = [[UIVisualEffectView alloc] self.shadowContainer = [[UIView alloc] init];
initWithEffect:[UIBlurEffect
effectWithStyle:UIBlurEffectStyleExtraLight]]; self.shadowImage =
self.tableViewContainer.contentView.backgroundColor = [[UIImageView alloc] initWithImage:StretchableImageNamed(@"menu_shadow")];
[UIColor colorWithHue:0 saturation:0 brightness:0.98 alpha:0.95]; self.shadowImage.translatesAutoresizingMaskIntoConstraints = NO;
[self.tableViewContainer.contentView [self.shadowContainer addSubview:self.shadowImage];
addSubview:self.presentedViewController.view]; AddSameConstraintsToSidesWithInsets(
self.shadowContainer, self.shadowImage,
LayoutSides::kTop | LayoutSides::kBottom | LayoutSides::kLeading |
LayoutSides::kTrailing,
ChromeDirectionalEdgeInsetsMake(kShadowMargin, kShadowMargin,
kShadowMargin, kShadowMargin));
self.tableViewContainer = [[UIView alloc] init];
self.tableViewContainer.backgroundColor =
[UIColor colorWithHue:0 saturation:0 brightness:0.98 alpha:1.0];
[self.tableViewContainer addSubview:self.presentedViewController.view];
self.tableViewContainer.translatesAutoresizingMaskIntoConstraints = NO;
self.tableViewContainer.layer.cornerRadius = kContainerCornerRadius; self.tableViewContainer.layer.cornerRadius = kContainerCornerRadius;
self.tableViewContainer.layer.masksToBounds = YES; self.tableViewContainer.layer.masksToBounds = YES;
self.tableViewContainer.clipsToBounds = YES; self.tableViewContainer.clipsToBounds = YES;
[self.containerView addSubview:self.tableViewContainer];
self.tableViewContainer.frame = [self frameOfPresentedViewInContainerView]; [self.shadowContainer addSubview:self.tableViewContainer];
AddSameConstraints(self.shadowContainer, self.tableViewContainer);
[self.containerView addSubview:self.shadowContainer];
self.shadowContainer.frame = [self frameOfPresentedViewInContainerView];
self.presentedViewController.view.autoresizingMask = self.presentedViewController.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
self.presentedViewController.view.frame = self.presentedViewController.view.frame = self.tableViewContainer.bounds;
self.tableViewContainer.contentView.bounds;
// Start the shadowImage with an alpha of 0.0 and animate it alongside the
// presentation transition. If the animation could not be queued, then simply
// leave the shadowImage's alpha as 1.0.
BOOL animationQueued = [self.presentedViewController.transitionCoordinator
animateAlongsideTransition:^(
id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
self.shadowImage.alpha = 1.0;
}
completion:nil];
self.shadowImage.alpha = animationQueued ? 0.0 : 1.0;
} }
- (void)containerViewWillLayoutSubviews { - (void)presentationTransitionDidEnd:(BOOL)completed {
self.tableViewContainer.frame = [self frameOfPresentedViewInContainerView]; if (!completed) {
[self cleanUpPresentationContainerViews];
}
}
- (void)dismissalTransitionWillBegin {
[self.presentedViewController.transitionCoordinator
animateAlongsideTransition:^(
id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
self.shadowImage.alpha = 0.0;
}
completion:nil];
} }
- (void)dismissalTransitionDidEnd:(BOOL)completed { - (void)dismissalTransitionDidEnd:(BOOL)completed {
if (completed) { if (completed) {
[self.tableViewContainer removeFromSuperview]; [self cleanUpPresentationContainerViews];
self.tableViewContainer = nil;
} }
} }
- (void)containerViewWillLayoutSubviews {
self.shadowContainer.frame = [self frameOfPresentedViewInContainerView];
}
#pragma mark - Private Methods
- (void)cleanUpPresentationContainerViews {
[self.tableViewContainer removeFromSuperview];
self.tableViewContainer = nil;
[self.shadowImage removeFromSuperview];
self.shadowImage = nil;
[self.shadowContainer removeFromSuperview];
self.shadowContainer = nil;
}
#pragma mark - Adaptivity #pragma mark - Adaptivity
- (UIModalPresentationStyle)adaptivePresentationStyleForTraitCollection: - (UIModalPresentationStyle)adaptivePresentationStyleForTraitCollection:
(UITraitCollection*)traitCollection { (UITraitCollection*)traitCollection {
if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact) { if (traitCollection.horizontalSizeClass == UIUserInterfaceSizeClassCompact &&
traitCollection.verticalSizeClass != UIUserInterfaceSizeClassCompact) {
return UIModalPresentationFullScreen; return UIModalPresentationFullScreen;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment