Commit ca5b8dd1 authored by Roberto Moura's avatar Roberto Moura Committed by Commit Bot

Add thumb strip files.

Addition of the thumb strip coordinator, pan gesture
handler and animatee classes. The handler handles user pan gestures that
reveal/hide the thumb strip and calls methods on other UI elements to be
animated during thumb strip transitions. The view revealing animatee is
a protocol to be implemented by UI objects that are animated in these
transitions.

Bug: 1094335
Change-Id: Iad91af43ea4956b47f6784ea138fe4d81a482489
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2313877Reviewed-by: default avatarMark Cogan <marq@chromium.org>
Reviewed-by: default avataredchin <edchin@chromium.org>
Commit-Queue: Roberto Moura <mouraroberto@google.com>
Auto-Submit: Roberto Moura <mouraroberto@google.com>
Cr-Commit-Position: refs/heads/master@{#799655}
parent 9d78e988
# Copyright 2020 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("//build/config/chrome_build.gni")
source_set("gestures") {
sources = [
"view_revealing_animatee.h",
"view_revealing_vertical_pan_handler.h",
"view_revealing_vertical_pan_handler.mm",
]
deps = [ "//base" ]
configs += [ "//build/config/compiler:enable_arc" ]
}
// Copyright 2020 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_GESTURES_VIEW_REVEALING_ANIMATEE_H_
#define IOS_CHROME_BROWSER_UI_GESTURES_VIEW_REVEALING_ANIMATEE_H_
// Protocol defining an interface to handle animations from the view revealing
// pan gesture handler.
@protocol ViewRevealingAnimatee <NSObject>
// Called inside an animation block to animate the revealing of the view. Takes
// as argument whether the view is currently revealed or not.
- (void)animateViewReveal:(BOOL)viewRevealed;
@end
#endif // IOS_CHROME_BROWSER_UI_GESTURES_VIEW_REVEALING_ANIMATEE_H_
// Copyright 2020 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_GESTURES_VIEW_REVEALING_VERTICAL_PAN_HANDLER_H_
#define IOS_CHROME_BROWSER_UI_GESTURES_VIEW_REVEALING_VERTICAL_PAN_HANDLER_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/gestures/view_revealing_animatee.h"
// Responsible for handling vertical pan gestures to reveal/hide a view behind
// another.
@interface ViewRevealingVerticalPanHandler : NSObject
- (instancetype)initWithHeight:(CGFloat)height;
// When creating a pan gesture, this method is passed as the action on the init
// method. It is called when a gesture starts, moves, or ends.
- (void)handlePanGesture:(UIPanGestureRecognizer*)gesture;
// Adds UI element to list of animated objects.
- (void)addAnimatee:(id<ViewRevealingAnimatee>)animatee;
// Height of the view that will be revealed.
@property(nonatomic, assign, readonly) CGFloat viewHeight;
@end
#endif // IOS_CHROME_BROWSER_UI_THUMB_STRIP_VIEW_REVEALING_VERTICAL_PAN_HANDLER_H_
// Copyright 2020 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/gestures/view_revealing_vertical_pan_handler.h"
#include "base/numerics/ranges.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The weight multiplier of the gesture velocity used in the equation that
// determines whether the translation and velocity of the gesture are enough
// to trigger revealing the view. The threshold is the percentage of the height
// of the view that must be "traveled" by the gesture to trigger the transition.
const CGFloat kVelocityWeight = 0.5f;
const CGFloat kRevealThreshold = 1 / 3.0f;
// Duration of the animation to reveal/hide the view.
const CGFloat kAnimationDuration = 0.25f;
} // namespace
@interface ViewRevealingVerticalPanHandler ()
// The property animator for revealing the view.
@property(nonatomic, strong) UIViewPropertyAnimator* animator;
// Whether the view is currently revealed or not.
@property(nonatomic, assign, getter=isViewRevealed, setter=setIsViewRevealed:)
BOOL viewRevealed;
// The progress of the animator before being interrupted.
@property(nonatomic, assign) CGFloat progressWhenInterrupted;
// Set of UI elements which are animated during thumb strip transitions.
@property(nonatomic, strong) NSMutableSet<id<ViewRevealingAnimatee>>* animatees;
@end
@implementation ViewRevealingVerticalPanHandler
- (instancetype)initWithHeight:(CGFloat)height {
if (self = [super init]) {
_viewRevealed = NO;
_viewHeight = height;
_animatees = [[NSMutableSet alloc] init];
}
return self;
}
- (void)handlePanGesture:(UIPanGestureRecognizer*)gesture {
CGFloat translationY = [gesture translationInView:gesture.view.superview].y;
if (gesture.state == UIGestureRecognizerStateBegan) {
[self panGestureBegan];
} else if (gesture.state == UIGestureRecognizerStateChanged) {
[self panGestureChangedWithTranslation:translationY];
} else if (gesture.state == UIGestureRecognizerStateEnded) {
CGFloat velocityY = [gesture velocityInView:gesture.view.superview].y;
[self panGestureEndedWithTranslation:translationY velocity:velocityY];
}
}
- (void)addAnimatee:(id<ViewRevealingAnimatee>)animatee {
[self.animatees addObject:animatee];
}
#pragma mark - Private Methods: Animating
// Called inside an animation block to animate all animatees.
- (void)animateViewReveal:(BOOL)viewRevealed {
for (id<ViewRevealingAnimatee> animatee in self.animatees) {
[animatee animateViewReveal:viewRevealed];
}
}
#pragma mark - Private Methods: Pan handling
// Returns whether the gesture's translation and velocity were enough to trigger
// revealing the view.
- (BOOL)attainedRevealThresholdWithTranslation:(CGFloat)translation
Velocity:(CGFloat)velocity {
return self.progressWhenInterrupted +
(translation + velocity * kVelocityWeight) / self.viewHeight >
kRevealThreshold;
}
// Returns whether the gesture's translation and velocity were enough to trigger
// hiding the view.
- (BOOL)attainedHideThresholdWithTranslation:(CGFloat)translation
Velocity:(CGFloat)velocity {
return self.progressWhenInterrupted +
(translation + velocity * kVelocityWeight) / (-self.viewHeight) >
kRevealThreshold;
}
// Initiate a transition if it isn't already running
- (void)animateTransitionIfNeeded {
if (self.animator.isRunning) {
self.animator.reversed = NO;
return;
}
// Create the transition to reveal the view.
auto animationBlock = ^() {
[self animateViewReveal:self.isViewRevealed];
};
auto completionBlock = ^(UIViewAnimatingPosition finalPosition) {
if (!self.animator.reversed) {
self.isViewRevealed = !self.isViewRevealed;
}
};
self.animator =
[[UIViewPropertyAnimator alloc] initWithDuration:kAnimationDuration
dampingRatio:1
animations:animationBlock];
[self.animator addCompletion:completionBlock];
}
// Handles the start of the pan gesture.
- (void)panGestureBegan {
[self animateTransitionIfNeeded];
[self.animator pauseAnimation];
self.progressWhenInterrupted = self.animator.fractionComplete;
}
// Handles the movement after the start of the gesture.
- (void)panGestureChangedWithTranslation:(CGFloat)translation {
CGFloat progress =
(self.isViewRevealed ? -1 : 1) * translation / self.viewHeight +
self.progressWhenInterrupted;
progress = base::ClampToRange<CGFloat>(progress, 0.0, 1.0);
self.animator.fractionComplete = progress;
}
// Handles the end of the gesture.
- (void)panGestureEndedWithTranslation:(CGFloat)translation
velocity:(CGFloat)velocity {
self.animator.reversed = YES;
if ((!self.isViewRevealed &&
[self attainedRevealThresholdWithTranslation:translation
Velocity:velocity]) ||
(self.isViewRevealed &&
[self attainedHideThresholdWithTranslation:translation
Velocity:velocity])) {
self.animator.reversed = NO;
}
[self.animator continueAnimationWithTimingParameters:nil durationFactor:1];
}
@end
# Copyright 2020 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("//build/config/chrome_build.gni")
source_set("thumb_strip") {
sources = [
"thumb_strip_coordinator.h",
"thumb_strip_coordinator.mm",
]
deps = [
"//base",
"//ios/chrome/browser/ui/coordinators:chrome_coordinators",
"//ios/chrome/browser/ui/gestures",
]
configs += [ "//build/config/compiler:enable_arc" ]
}
// Copyright 2020 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_THUMB_STRIP_THUMB_STRIP_COORDINATOR_H_
#define IOS_CHROME_BROWSER_UI_THUMB_STRIP_THUMB_STRIP_COORDINATOR_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/coordinators/chrome_coordinator.h"
@class ViewRevealingVerticalPanHandler;
// Coordinator for the thumb strip, which is a 1-row horizontal display of tab
// miniatures above the toolbar.
@interface ThumbStripCoordinator : ChromeCoordinator
// The thumb strip's pan gesture handler.
@property(nonatomic, strong) ViewRevealingVerticalPanHandler* panHandler;
@end
#endif // IOS_CHROME_BROWSER_UI_THUMB_STRIP_THUMB_STRIP_COORDINATOR_H_
// Copyright 2020 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/thumb_strip/thumb_strip_coordinator.h"
#import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Height of the view that is revealed. The thumb strip has a height equal to a
// small grid cell + edge insets (top and bottm) from thumb strip layout.
const CGFloat kThumbStripHeight = 168.0f + 22.0f + 22.0f;
} // namespace
@implementation ThumbStripCoordinator
#pragma mark - ChromeCoordinator
- (void)start {
self.panHandler = [[ViewRevealingVerticalPanHandler alloc]
initWithHeight:kThumbStripHeight];
}
- (void)stop {
self.panHandler = nil;
}
@end
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