Skip to content

Commit

Permalink
[iOS] Updated foreground new-tab animation for UIRefresh
Browse files Browse the repository at this point in the history
This CL replaces the "open new tab" animation from the BVC for UIRefresh.

The animation itself is implemented inside an animation view which is inserted at the front of the BVC's content area; this will put the animation behind the bottom toolbar (or behind the toolbar in landscape).

To avoid too much refactoring, this CL uses most of the setup code for the existing animation. To patch a visual glitch with that code, a snapshot of the top toolbar is inserted into the content area while the animation is running.

Bug: 851872
Cq-Include-Trybots: luci.chromium.try:ios-simulator-full-configs;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: I12cb1805d6123ae0cdebcc7ce739f8ce51e3cfca
Reviewed-on: https://chromium-review.googlesource.com/1143394
Commit-Queue: Mark Cogan <marq@chromium.org>
Reviewed-by: Gauthier Ambard <gambard@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#576830}(cherry picked from commit aad2c85)
Reviewed-on: https://chromium-review.googlesource.com/1149780
Reviewed-by: Mark Cogan <marq@chromium.org>
Cr-Commit-Position: refs/branch-heads/3497@{#64}
Cr-Branched-From: 271eaf5-refs/heads/master@{#576753}
  • Loading branch information
marcq authored and Mark Cogan committed Jul 25, 2018
1 parent 923b972 commit fb463bc
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 34 deletions.
115 changes: 81 additions & 34 deletions ios/chrome/browser/ui/browser_view_controller.mm
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@
#import "ios/chrome/browser/ui/static_content/static_html_native_content.h"
#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_controller.h"
#import "ios/chrome/browser/ui/tabs/background_tab_animation_view.h"
#import "ios/chrome/browser/ui/tabs/foreground_tab_animation_view.h"
#import "ios/chrome/browser/ui/tabs/requirements/tab_strip_constants.h"
#import "ios/chrome/browser/ui/tabs/requirements/tab_strip_presentation.h"
#import "ios/chrome/browser/ui/tabs/tab_strip_legacy_coordinator.h"
Expand Down Expand Up @@ -5264,6 +5265,8 @@ - (void)animateNewTab:(Tab*)tab
UIView* newPage = nil;
CGFloat offset = 0;
GURL tabURL = tab.webState->GetLastCommittedURL();
// Toolbar snapshot is only used for the UIRefresh animation.
UIView* toolbarSnapshot;
// Vislble URL should be more correct here than last committed, but for
// safety, limiting the scope only to WKBasedNavigationManager, which needs
// it to correctly animate NTP. See https://crbug.com/819606.
Expand All @@ -5272,10 +5275,24 @@ - (void)animateNewTab:(Tab*)tab
if (tabURL == kChromeUINewTabURL && !_isOffTheRecord &&
![self canShowTabStrip]) {
offset = 0;
// Temporary expand content area to take whole view space. Otherwise the
// animated NTP will be clipped by content area bound. Previous frame will
// be reset back on the animation completion.
self.contentArea.frame = self.view.frame;
if (IsUIRefreshPhase1Enabled()) {
// Add a snapshot of the primary toolbar to the background as the
// animation runs.
UIViewController* toolbarViewController =
self.primaryToolbarCoordinator.viewController;
toolbarSnapshot =
[toolbarViewController.view snapshotViewAfterScreenUpdates:NO];
toolbarSnapshot.frame =
[self.contentArea convertRect:toolbarSnapshot.frame
fromView:self.view];
[self.contentArea addSubview:toolbarSnapshot];
} else {
// Temporarily expand content area to take whole view space. Otherwise the
// animated NTP will be clipped by content area bound. Previous frame will
// be reset back on the animation completion.
self.contentArea.frame = self.view.frame;
}

newPage = tab.view;
newPage.userInteractionEnabled = NO;
if (base::FeatureList::IsEnabled(
Expand Down Expand Up @@ -5305,39 +5322,69 @@ - (void)animateNewTab:(Tab*)tab
}
newPageOffset = newPage.frame.origin.y;

[self.contentArea addSubview:newPage];
// Cleanup steps needed for both UI Refresh and stack-view style animations.
auto commonCompletion = ^{
tab.view.frame = self.contentArea.bounds;
newPage.userInteractionEnabled = YES;
self.inNewTabAnimation = NO;
// Use the model's currentTab here because it is possible that it can
// be reset to a new value before the new Tab animation finished (e.g.
// if another Tab shows a dialog via |dialogPresenter|). However, that
// tab's view hasn't been displayed yet because it was in a new tab
// animation.
Tab* currentTab = [_model currentTab];
if (currentTab) {
[self tabSelected:currentTab notifyToolbar:NO];
}
if (completion)
completion();

if (self.foregroundTabWasAddedCompletionBlock) {
self.foregroundTabWasAddedCompletionBlock();
self.foregroundTabWasAddedCompletionBlock = nil;
}
};

CGPoint origin = [self lastTapPoint];
page_animation_util::AnimateInPaperWithAnimationAndCompletion(
newPage, -newPageOffset, offset, origin, _isOffTheRecord, NULL, ^{
tab.view.frame = self.contentArea.bounds;
newPage.userInteractionEnabled = YES;
[newPage removeFromSuperview];
self.inNewTabAnimation = NO;
// Use the model's currentTab here because it is possible that it can
// be reset to a new value before the new Tab animation finished (e.g.
// if another Tab shows a dialog via |dialogPresenter|). However, that
// tab's view hasn't been displayed yet because it was in a new tab
// animation.
Tab* currentTab = [_model currentTab];
if (currentTab) {
[self tabSelected:currentTab notifyToolbar:NO];
}
completion();

if (self.foregroundTabWasAddedCompletionBlock) {
self.foregroundTabWasAddedCompletionBlock();
self.foregroundTabWasAddedCompletionBlock = nil;
}
// UI Refresh animation.
if (IsUIRefreshPhase1Enabled()) {
CGRect frame = self.view.bounds;
frame.origin.y += StatusBarHeight();
frame = [self.contentArea convertRect:frame fromView:self.view];
ForegroundTabAnimationView* animatedView =
[[ForegroundTabAnimationView alloc] initWithFrame:frame];
animatedView.contentView = newPage;
__weak UIView* weakAnimatedView = animatedView;
auto completionBlock = ^() {
[weakAnimatedView removeFromSuperview];
[toolbarSnapshot removeFromSuperview];
commonCompletion();
};
[self.contentArea addSubview:animatedView];
[animatedView animateFrom:origin withCompletion:completionBlock];
return;
}

// Restore content area frame, which was resized to fullscreen for
// NTP opening animation.
CGRect contentAreaFrame = self.view.bounds;
if (!self.usesFullscreenContainer) {
contentAreaFrame.origin.y += StatusBarHeight();
contentAreaFrame.size.height -= StatusBarHeight();
}
self.contentArea.frame = contentAreaFrame;
});
// Else, stack view-style animation.
[self.contentArea addSubview:newPage];

auto animationCompletion = ^{
[newPage removeFromSuperview];
commonCompletion();
// Restore content area frame, which was resized to fullscreen for
// NTP opening animation.
CGRect contentAreaFrame = self.view.bounds;
if (!self.usesFullscreenContainer) {
contentAreaFrame.origin.y += StatusBarHeight();
contentAreaFrame.size.height -= StatusBarHeight();
}
self.contentArea.frame = contentAreaFrame;
};

page_animation_util::AnimateInPaperWithAnimationAndCompletion(
newPage, -newPageOffset, offset, origin, _isOffTheRecord, NULL,
animationCompletion);
}

- (void)animateNewTabInBackgroundFromPoint:(CGPoint)originPoint
Expand Down
2 changes: 2 additions & 0 deletions ios/chrome/browser/ui/tabs/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ source_set("tabs") {
sources = [
"background_tab_animation_view.h",
"background_tab_animation_view.mm",
"foreground_tab_animation_view.h",
"foreground_tab_animation_view.mm",
"tab_strip_controller+placeholder_view.h",
"tab_strip_controller.h",
"tab_strip_controller.mm",
Expand Down
26 changes: 26 additions & 0 deletions ios/chrome/browser/ui/tabs/foreground_tab_animation_view.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// 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_TABS_FOREGROUND_TAB_ANIMATION_VIEW_H_
#define IOS_CHROME_BROWSER_UI_TABS_FOREGROUND_TAB_ANIMATION_VIEW_H_

#import <UIKit/UIKit.h>

// View used to contain an animation of a new tab opening in the foreground.
// A content subview animates to fill the view while the background fades to
// black.
@interface ForegroundTabAnimationView : UIView

// The content view (typically the new tab's view) to animate.
@property(nonatomic, strong) UIView* contentView;

// Starts a New Tab animation in |parentView|, from |originPoint| with
// a |completion| block. The new tab will scale up and move from the direction
// if |originPoint| to the center of the reciever. |originPoint| must be in
// UIWindow coordinates.
- (void)animateFrom:(CGPoint)originPoint withCompletion:(void (^)())completion;

@end

#endif // IOS_CHROME_BROWSER_UI_TABS_FOREGROUND_TAB_ANIMATION_VIEW_H_
130 changes: 130 additions & 0 deletions ios/chrome/browser/ui/tabs/foreground_tab_animation_view.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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/tabs/foreground_tab_animation_view.h"

#import "ios/chrome/browser/ui/util/property_animator_group.h"

#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif

namespace {
const NSTimeInterval kAnimationDuration = 0.75;
const CGFloat kTabMotionDamping = 0.7;
const CGFloat kTabFadeInRelativeDuration = 0.4;
const CGFloat kBackgroundFadeRelativeDuration = 0.33;
const CGFloat kCornerRoundingRelativeDuration = 0.33;
const CGFloat kInitialTabScale = 0.75;
const CGFloat kInitialTabCornerRadius = 26.0;
const CGFloat kPositionCoefficient = 0.25;
} // namespace

@implementation ForegroundTabAnimationView

@synthesize contentView = _contentView;

- (void)setContentView:(UIView*)contentView {
[_contentView removeFromSuperview];
[self addSubview:contentView];
_contentView = contentView;
}

- (void)animateFrom:(CGPoint)originPoint withCompletion:(void (^)())completion {
CGPoint origin = [self convertPoint:originPoint fromView:nil];
self.contentView.frame = self.bounds;
self.backgroundColor = UIColor.clearColor;

// Translate the content view part of the way from the center of this view to
// |originPoint|.
CGFloat dx = kPositionCoefficient * (origin.x - self.contentView.center.x);
CGFloat dy = kPositionCoefficient * (origin.y - self.contentView.center.y);

CGAffineTransform transform = self.contentView.transform;
transform = CGAffineTransformTranslate(transform, dx, dy);
transform =
CGAffineTransformScale(transform, kInitialTabScale, kInitialTabScale);

self.contentView.transform = transform;
self.contentView.alpha = 0;
self.contentView.layer.cornerRadius = kInitialTabCornerRadius;
self.contentView.clipsToBounds = YES;

// Animation components.
auto tabResizeAnimation = ^{
self.contentView.transform = CGAffineTransformIdentity;
};
auto tabFadeAnimation = ^{
self.contentView.alpha = 1.0;
};
auto backgroundFadeAnimation = ^{
self.backgroundColor = UIColor.blackColor;
};
auto cornerAnimation = ^{
self.contentView.layer.cornerRadius = 0.0;
};

PropertyAnimatorGroup* animations = [[PropertyAnimatorGroup alloc] init];

// Motion animation runs for the whole duration with a spring effect.
// Because of the spring effect, the total duration needs to be quite long
// or else the animation will feel very abrupt.
UIViewPropertyAnimator* motionAnimation =
[[UIViewPropertyAnimator alloc] initWithDuration:kAnimationDuration
dampingRatio:kTabMotionDamping
animations:tabResizeAnimation];

// Tab fades in over the first half of the overall animation, easing out (so
// most of the fade happens sooner).
auto tabfadeAnimationKeyframes = ^{
[UIView addKeyframeWithRelativeStartTime:0
relativeDuration:kTabFadeInRelativeDuration
animations:tabFadeAnimation];

};
UIViewPropertyAnimator* tabfadeAnimation = [[UIViewPropertyAnimator alloc]
initWithDuration:kAnimationDuration
curve:UIViewAnimationCurveEaseOut
animations:^{
[UIView animateKeyframesWithDuration:kAnimationDuration
delay:0
options:0
animations:tabfadeAnimationKeyframes
completion:nil];
}];

// Additional animations happen in the first third of the overall animation,
// and are linear.
auto additionalAnimationsKeyframes = ^{
[UIView addKeyframeWithRelativeStartTime:0
relativeDuration:kBackgroundFadeRelativeDuration
animations:backgroundFadeAnimation];
[UIView addKeyframeWithRelativeStartTime:0
relativeDuration:kCornerRoundingRelativeDuration
animations:cornerAnimation];
};
UIViewPropertyAnimator* additionalAnimations = [[UIViewPropertyAnimator alloc]
initWithDuration:kAnimationDuration
curve:UIViewAnimationCurveLinear
animations:^{
[UIView animateKeyframesWithDuration:kAnimationDuration
delay:0
options:0
animations:additionalAnimationsKeyframes
completion:nil];
}];

[animations addAnimator:motionAnimation];
[animations addAnimator:tabfadeAnimation];
[animations addAnimator:additionalAnimations];

[animations addCompletion:^(UIViewAnimatingPosition finalPosition) {
self.contentView.clipsToBounds = NO;
completion();
}];

[animations startAnimation];
}

@end

0 comments on commit fb463bc

Please sign in to comment.