Commit 0d83f1cb authored by Gauthier Ambard's avatar Gauthier Ambard Committed by Commit Bot

[iOS] Change NTP fakebox animation

This CL changes the fakebox animation to have something smoother.
The animation should be one transition directly from the fake omnibox
to the real omnibox, instead of the two steps process we currently have.

Bug: 910753
Change-Id: Iac51c08b49db09b3a2e0041e236af6cba90fa55c
Reviewed-on: https://chromium-review.googlesource.com/c/1473231Reviewed-by: default avatarStepan Khapugin <stkhapugin@chromium.org>
Reviewed-by: default avatarJustin Cohen <justincohen@chromium.org>
Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Commit-Queue: Gauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636734}
parent 0bbfe5f3
......@@ -123,6 +123,8 @@ source_set("content_suggestions_ui") {
"//ios/chrome/browser/ui/content_suggestions/identifier",
"//ios/chrome/browser/ui/list_model",
"//ios/chrome/browser/ui/ntp",
"//ios/chrome/browser/ui/omnibox:omnibox_internal",
"//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
"//ios/chrome/browser/ui/overscroll_actions",
"//ios/chrome/browser/ui/toolbar/buttons",
"//ios/chrome/browser/ui/toolbar/public",
......@@ -252,7 +254,7 @@ source_set("eg_tests") {
"//ios/chrome/browser/ui/ntp:ntp_controller",
"//ios/chrome/browser/ui/settings",
"//ios/chrome/browser/ui/tab_grid:egtest_support",
"//ios/chrome/browser/ui/toolbar/buttons:buttons",
"//ios/chrome/browser/ui/toolbar/buttons",
"//ios/chrome/browser/ui/toolbar/public",
"//ios/chrome/test/app:test_support",
"//ios/chrome/test/base:base",
......
......@@ -13,9 +13,11 @@
// Moves the tiles down, by setting the content offset of the collection to 0.
- (void)shiftTilesDown;
// Moves the tiles up by pinning the omnibox to the top. Completion called only
// if scrolled to top.
- (void)shiftTilesUpWithCompletionBlock:(ProceduralBlock)completionBlock;
// Moves the tiles up by pinning the omnibox to the top. |completion| is called
// when the collection is scrolled to top. |animations| is called only if it is
// not yet scrolled to the top.
- (void)shiftTilesUpWithAnimations:(ProceduralBlock)animations
completion:(ProceduralBlock)completion;
// Notifies the collection that its layout has changed and should be
// invalidated.
- (void)invalidateLayout;
......
......@@ -161,10 +161,6 @@ void configureSearchHintLabel(UILabel* searchHintLabel,
[searchHintLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[searchTapTarget addSubview:searchHintLabel];
[searchHintLabel.centerXAnchor
constraintEqualToAnchor:searchTapTarget.centerXAnchor]
.active = YES;
[searchHintLabel setText:l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT)];
if (base::i18n::IsRTL()) {
[searchHintLabel setTextAlignment:NSTextAlignmentRight];
......
......@@ -102,7 +102,8 @@ initWithCollectionController:
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void)shiftTilesUpWithCompletionBlock:(ProceduralBlock)completion {
- (void)shiftTilesUpWithAnimations:(ProceduralBlock)animations
completion:(ProceduralBlock)completion {
// Add gesture recognizer to collection view when the omnibox is focused.
[self.collectionView addGestureRecognizer:self.tapGestureRecognizer];
......@@ -133,6 +134,8 @@ initWithCollectionController:
[UIView animateWithDuration:kShiftTilesUpAnimationDuration
animations:^{
if (self.collectionView.contentOffset.y < pinnedOffsetY) {
if (animations)
animations();
// Changing the contentOffset of the collection results in a scroll
// and a change in the constraints of the header.
self.collectionView.contentOffset = CGPointMake(0, pinnedOffsetY);
......
......@@ -58,7 +58,7 @@ TEST_F(ContentSuggestionsHeaderSynchronizerTest, shiftUp) {
OCMExpect([collectionController setScrolledToTop:YES]);
// Action.
[Synchronizer() shiftTilesUpWithCompletionBlock:nil];
[Synchronizer() shiftTilesUpWithAnimations:nil completion:nil];
// Tests.
EXPECT_OCMOCK_VERIFY(collectionController);
......
......@@ -18,6 +18,18 @@
// Voice search button.
@property(nonatomic, strong, readonly) UIButton* voiceSearchButton;
// Fake cancel button, used for animations. Hidden by default.
@property(nonatomic, strong) UIView* cancelButton;
// Fake omnibox, used for animations. Hidden by default.
@property(nonatomic, strong) UIView* omnibox;
@property(nonatomic, strong)
NSLayoutConstraint* fakeLocationBarLeadingConstraint;
@property(nonatomic, strong)
NSLayoutConstraint* fakeLocationBarTrailingConstraint;
@property(nonatomic, strong) UIView* fakeLocationBar;
@property(nonatomic, strong) UILabel* searchHintLabel;
// Adds the |toolbarView| to the view implementing this protocol.
// Can only be added once.
- (void)addToolbarView:(UIView*)toolbarView;
......
......@@ -7,9 +7,13 @@
#import <UIKit/UIKit.h>
#include "base/logging.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/UIView+SizeClassSupport.h"
#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_collection_utils.h"
#import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_container_view.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_text_field_ios.h"
#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button_factory.h"
#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
......@@ -18,6 +22,7 @@
#import "ios/chrome/browser/ui/util/named_guide_util.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "ui/base/l10n/l10n_util.h"
#import "ui/gfx/ios/NSString+CrStringDrawing.h"
#import "ui/gfx/ios/uikit_util.h"
......@@ -55,33 +60,14 @@ CGFloat ToolbarHeight() {
@property(nonatomic, strong) NSLayoutConstraint* fakeLocationBarTopConstraint;
@property(nonatomic, strong)
NSLayoutConstraint* fakeLocationBarHeightConstraint;
@property(nonatomic, strong)
NSLayoutConstraint* fakeLocationBarLeadingConstraint;
@property(nonatomic, strong)
NSLayoutConstraint* fakeLocationBarTrailingConstraint;
@property(nonatomic, strong) NSLayoutConstraint* fakeToolbarTopConstraint;
@property(nonatomic, strong) NSLayoutConstraint* hintLabelLeadingConstraint;
@property(nonatomic, strong) NSLayoutConstraint* voiceSearchTrailingConstraint;
@property(nonatomic, strong) UIView* fakeLocationBar;
@property(nonatomic, strong) UILabel* searchHintLabel;
@end
@implementation ContentSuggestionsHeaderView
@synthesize fakeLocationBar = _fakeLocationBar;
@synthesize fakeLocationBarTopConstraint = _fakeLocationBarTopConstraint;
@synthesize fakeLocationBarHeightConstraint = _fakeLocationBarHeightConstraint;
@synthesize fakeLocationBarLeadingConstraint =
_fakeLocationBarLeadingConstraint;
@synthesize fakeLocationBarTrailingConstraint =
_fakeLocationBarTrailingConstraint;
@synthesize fakeToolbarTopConstraint = _fakeToolbarTopConstraint;
@synthesize voiceSearchTrailingConstraint = _voiceSearchTrailingConstraint;
@synthesize hintLabelLeadingConstraint = _hintLabelLeadingConstraint;
@synthesize toolBarView = _toolBarView;
@synthesize searchHintLabel = _searchHintLabel;
#pragma mark - Public
- (instancetype)initWithFrame:(CGRect)frame {
......@@ -128,6 +114,39 @@ CGFloat ToolbarHeight() {
// Fake location bar.
[fakeToolbarContentView addSubview:self.fakeLocationBar];
// Omnibox, used for animations.
// TODO(crbug.com/936811): See if it is possible to share some initialization
// code with the real Omnibox.
UIColor* color = [UIColor colorWithWhite:0 alpha:kOmniboxPlaceholderAlpha];
OmniboxContainerView* omnibox =
[[OmniboxContainerView alloc] initWithFrame:CGRectZero
textColor:color
textFieldTint:color
iconTint:color];
omnibox.textField.placeholderTextColor = color;
omnibox.textField.placeholder =
l10n_util::GetNSString(IDS_OMNIBOX_EMPTY_HINT);
[omnibox.textField setText:@""];
omnibox.translatesAutoresizingMaskIntoConstraints = NO;
[searchField addSubview:omnibox];
AddSameConstraints(omnibox, self.fakeLocationBar);
omnibox.textField.userInteractionEnabled = NO;
omnibox.hidden = YES;
self.omnibox = omnibox;
// Cancel button, used in animation.
ToolbarButtonFactory* factory =
[[ToolbarButtonFactory alloc] initWithStyle:NORMAL];
self.cancelButton = [factory cancelButton];
[searchField addSubview:self.cancelButton];
self.cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.cancelButton.centerYAnchor
constraintEqualToAnchor:self.fakeLocationBar.centerYAnchor],
[self.cancelButton.leadingAnchor
constraintEqualToAnchor:self.fakeLocationBar.trailingAnchor],
]];
// Hint label.
self.searchHintLabel = [[UILabel alloc] init];
content_suggestions::configureSearchHintLabel(self.searchHintLabel,
......@@ -136,6 +155,8 @@ CGFloat ToolbarHeight() {
constraintGreaterThanOrEqualToAnchor:[searchField leadingAnchor]
constant:ntp_header::kHintLabelSidePadding];
[NSLayoutConstraint activateConstraints:@[
[self.searchHintLabel.centerXAnchor
constraintEqualToAnchor:self.fakeLocationBar.centerXAnchor],
self.hintLabelLeadingConstraint,
[self.searchHintLabel.heightAnchor
constraintEqualToAnchor:self.fakeLocationBar.heightAnchor
......@@ -181,14 +202,21 @@ CGFloat ToolbarHeight() {
self.fakeLocationBarHeightConstraint,
]];
// The voice search button should always be at least inside the fake omnibox.
// When the fake omnibox is shrinked, the position from the trailing side of
// the search field should yield.
self.voiceSearchTrailingConstraint = [self.voiceSearchButton.trailingAnchor
constraintEqualToAnchor:[searchField trailingAnchor]];
self.voiceSearchTrailingConstraint.priority = UILayoutPriorityDefaultHigh + 1;
[NSLayoutConstraint activateConstraints:@[
[self.voiceSearchButton.centerYAnchor
constraintEqualToAnchor:self.fakeLocationBar.centerYAnchor],
[self.searchHintLabel.trailingAnchor
constraintLessThanOrEqualToAnchor:self.voiceSearchButton.leadingAnchor],
self.voiceSearchTrailingConstraint
self.voiceSearchTrailingConstraint,
[self.voiceSearchButton.trailingAnchor
constraintLessThanOrEqualToAnchor:self.fakeLocationBar.trailingAnchor],
]];
}
......
......@@ -40,6 +40,11 @@ using base::UserMetricsAction;
@interface ContentSuggestionsHeaderViewController ()
// If YES the animations of the fake omnibox triggered when the collection is
// scrolled (expansion) are disabled. This is used for the fake omnibox focus
// animations so the constraints aren't changed while the ntp is scrolled.
@property(nonatomic, assign) BOOL disableScrollAnimation;
// |YES| when notifications indicate the omnibox is focused.
@property(nonatomic, assign, getter=isOmniboxFocused) BOOL omniboxFocused;
......@@ -154,6 +159,9 @@ using base::UserMetricsAction;
}
}
if (self.disableScrollAnimation)
return;
[self.headerView updateSearchFieldWidth:self.fakeOmniboxWidthConstraint
height:self.fakeOmniboxHeightConstraint
topMargin:self.fakeOmniboxTopMarginConstraint
......@@ -200,6 +208,10 @@ using base::UserMetricsAction;
return AlignValueToPixel(offsetY);
}
- (void)loadView {
self.view = [[ContentSuggestionsHeaderView alloc] init];
}
- (CGFloat)headerHeight {
return content_suggestions::heightForLogoHeader(
self.logoIsShowing, self.promoCanShow, YES, [self topInset]);
......@@ -209,7 +221,8 @@ using base::UserMetricsAction;
- (UIView*)headerForWidth:(CGFloat)width {
if (!self.headerView) {
self.headerView = [[ContentSuggestionsHeaderView alloc] init];
self.headerView =
base::mac::ObjCCastStrict<ContentSuggestionsHeaderView>(self.view);
[self addFakeTapView];
[self addFakeOmnibox];
......@@ -416,13 +429,69 @@ using base::UserMetricsAction;
}
- (void)shiftTilesUp {
if (self.disableScrollAnimation)
return;
void (^animations)() = nil;
if (![self.delegate isScrolledToTop]) {
// Only trigger the fake omnibox animation if the header isn't scrolled to
// the top. Otherwise just rely on the normal animation.
self.disableScrollAnimation = YES;
[self.dispatcher focusOmniboxNoAnimation];
NamedGuide* omniboxGuide = [NamedGuide guideWithName:kOmniboxGuide
view:self.headerView];
// Layout the owning view to make sure that the constrains are applied.
[omniboxGuide.owningView layoutIfNeeded];
self.headerView.omnibox.hidden = NO;
self.headerView.cancelButton.hidden = NO;
self.headerView.omnibox.alpha = 0;
self.headerView.cancelButton.alpha = 0;
animations = ^{
// Make sure that the offset is after the pinned offset to have the fake
// omnibox taking the full width.
CGFloat offset = 9000;
[self.headerView
updateSearchFieldWidth:self.fakeOmniboxWidthConstraint
height:self.fakeOmniboxHeightConstraint
topMargin:self.fakeOmniboxTopMarginConstraint
forOffset:offset
screenWidth:self.headerView.bounds.size.width
safeAreaInsets:self.view.safeAreaInsets];
self.fakeOmniboxWidthConstraint.constant =
self.headerView.bounds.size.width;
[self.headerView layoutIfNeeded];
CGRect omniboxFrameInFakebox =
[[omniboxGuide owningView] convertRect:[omniboxGuide layoutFrame]
toView:self.fakeOmnibox];
self.headerView.fakeLocationBarLeadingConstraint.constant =
omniboxFrameInFakebox.origin.x;
self.headerView.fakeLocationBarTrailingConstraint.constant = -(
self.fakeOmnibox.bounds.size.width -
(omniboxFrameInFakebox.origin.x + omniboxFrameInFakebox.size.width));
self.headerView.voiceSearchButton.alpha = 0;
self.headerView.cancelButton.alpha = 0.7;
self.headerView.omnibox.alpha = 1;
self.headerView.searchHintLabel.alpha = 0;
[self.headerView layoutIfNeeded];
};
}
void (^completionBlock)() = ^{
self.headerView.omnibox.hidden = YES;
self.headerView.cancelButton.hidden = YES;
self.headerView.searchHintLabel.alpha = 1;
self.headerView.voiceSearchButton.alpha = 1;
self.disableScrollAnimation = NO;
[self.dispatcher fakeboxFocused];
if (IsSplitToolbarMode()) {
[self.dispatcher onFakeboxAnimationComplete];
}
};
[self.collectionSynchronizer shiftTilesUpWithCompletionBlock:completionBlock];
[self.collectionSynchronizer shiftTilesUpWithAnimations:animations
completion:completionBlock];
}
- (CGFloat)topInset {
......
......@@ -52,6 +52,7 @@ source_set("location_bar") {
"//ios/chrome/browser/ui/ntp:util",
"//ios/chrome/browser/ui/omnibox:omnibox",
"//ios/chrome/browser/ui/omnibox:omnibox_internal",
"//ios/chrome/browser/ui/omnibox:omnibox_popup_shared",
"//ios/chrome/browser/ui/omnibox:omnibox_util",
"//ios/chrome/browser/ui/omnibox/popup",
"//ios/chrome/browser/ui/orchestrator:orchestrator",
......
......@@ -7,6 +7,7 @@
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
#import "ios/chrome/browser/ui/location_bar/extended_touch_target_button.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
......@@ -67,7 +68,8 @@ const CGFloat kButtonTrailingSpacing = 10;
[[LocationBarSteadyViewColorScheme alloc] init];
scheme.fontColor = [UIColor colorWithWhite:0 alpha:0.7];
scheme.placeholderColor = [UIColor colorWithWhite:0 alpha:0.3];
scheme.placeholderColor = [UIColor colorWithWhite:0
alpha:kOmniboxPlaceholderAlpha];
scheme.trailingButtonColor = [UIColor colorWithWhite:0 alpha:0.7];
return scheme;
......
......@@ -5,9 +5,11 @@
#ifndef IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONSTANTS_H_
#define IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONSTANTS_H_
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
// The a11y identifier for the shortcuts table view cell in the omnibox popup.
extern NSString* const kShortcutsAccessibilityIdentifier;
extern const CGFloat kOmniboxPlaceholderAlpha;
#endif // IOS_CHROME_BROWSER_UI_OMNIBOX_OMNIBOX_CONSTANTS_H_
......@@ -9,3 +9,5 @@
#endif
NSString* const kShortcutsAccessibilityIdentifier = @"OmniboxShortcuts";
const CGFloat kOmniboxPlaceholderAlpha = 0.3;
......@@ -10,6 +10,7 @@
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/commands/browser_commands.h"
#import "ios/chrome/browser/ui/commands/load_query_commands.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_container_view.h"
#import "ios/chrome/browser/ui/toolbar/public/omnibox_focuser.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
......@@ -166,8 +167,9 @@ const CGFloat kClearButtonSize = 28.0f;
// Tint color for the textfield placeholder and the clear button.
- (UIColor*)placeholderAndClearButtonColor {
return self.incognito ? [UIColor colorWithWhite:1 alpha:0.5]
: [UIColor colorWithWhite:0 alpha:0.3];
return self.incognito
? [UIColor colorWithWhite:1 alpha:0.5]
: [UIColor colorWithWhite:0 alpha:kOmniboxPlaceholderAlpha];
}
#pragma mark notification callbacks
......
......@@ -47,6 +47,8 @@
@property(nonatomic, strong) LocationBarCoordinator* locationBarCoordinator;
// Orchestrator for the expansion animation.
@property(nonatomic, strong) OmniboxFocusOrchestrator* orchestrator;
// Whether the omnibox focusing should happen with animation.
@property(nonatomic, assign) BOOL enableAnimationsForOmniboxFocus;
@end
......@@ -66,6 +68,8 @@
if (self.started)
return;
self.enableAnimationsForOmniboxFocus = YES;
[self.commandDispatcher startDispatchingToTarget:self
forProtocol:@protocol(FakeboxFocuser)];
......@@ -141,7 +145,7 @@
transitionToStateOmniboxFocused:focused
toolbarExpanded:focused && !IsRegularXRegularSizeClass(
self.viewController)
animated:YES];
animated:self.enableAnimationsForOmniboxFocus];
}
#pragma mark - PrimaryToolbarViewControllerDelegate
......@@ -166,6 +170,12 @@
#pragma mark - FakeboxFocuser
- (void)focusOmniboxNoAnimation {
self.enableAnimationsForOmniboxFocus = NO;
[self fakeboxFocused];
self.enableAnimationsForOmniboxFocus = YES;
}
- (void)fakeboxFocused {
[self.locationBarCoordinator focusOmniboxFromFakebox];
}
......
......@@ -98,6 +98,7 @@
[self.view removeFakeOmniboxTarget];
}
}
#pragma mark - UIViewController
- (void)loadView {
......
......@@ -10,6 +10,8 @@
// This protocol provides callbacks for focusing and blurring the fake omnibox
// on NTP.
@protocol FakeboxFocuser
// Focuses the omnibox without animations.
- (void)focusOmniboxNoAnimation;
// Give focus to the omnibox, but indicate that the focus event was initiated
// from the fakebox on the Google landing page.
- (void)fakeboxFocused;
......
......@@ -213,7 +213,8 @@ UIView* SubviewWithAccessibilityIdentifier(NSString* accessibilityID,
}
+ (id<GREYMatcher>)omnibox {
return grey_kindOfClass([OmniboxTextFieldIOS class]);
return grey_allOf(grey_kindOfClass([OmniboxTextFieldIOS class]),
grey_userInteractionEnabled(), nil);
}
+ (id<GREYMatcher>)defocusedLocationView {
......
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