Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(262)

Side by Side Diff: ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.mm

Issue 2709233003: Add haptic feedback for swipe-to-go-back. (Closed)
Patch Set: File is ARC. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h" 5 #import "ios/chrome/browser/ui/side_swipe/side_swipe_navigation_view.h"
6 6
7 #include <cmath> 7 #include <cmath>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/mac/objc_property_releaser.h" 10 #include "base/mac/objc_property_releaser.h"
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 59
60 // The final scale of the selection bubble when the threshold is met. 60 // The final scale of the selection bubble when the threshold is met.
61 const CGFloat kSelectionAnimationScale = 23; 61 const CGFloat kSelectionAnimationScale = 23;
62 62
63 // The duration of the animations played when the threshold is met. 63 // The duration of the animations played when the threshold is met.
64 const CGFloat kSelectionAnimationDuration = 0.5; 64 const CGFloat kSelectionAnimationDuration = 0.5;
65 } 65 }
66 66
67 @interface SideSwipeNavigationView () { 67 @interface SideSwipeNavigationView () {
68 @private 68 @private
69 // Has the current swipe gone past the point where the action would trigger?
70 // Will be reset to NO if it recedes before that point (ie, not a latch).
71 BOOL thresholdTriggered_;
69 72
70 // The back or forward sprite image. 73 // The back or forward sprite image.
71 base::scoped_nsobject<UIImageView> arrowView_; 74 base::scoped_nsobject<UIImageView> arrowView_;
72 75
73 // The selection bubble. 76 // The selection bubble.
74 CAShapeLayer* selectionCircleLayer_; 77 CAShapeLayer* selectionCircleLayer_;
75 78
76 // If |NO| this is an edge gesture and navigation isn't possible. Don't show 79 // If |NO| this is an edge gesture and navigation isn't possible. Don't show
77 // arrows and bubbles and don't allow navigate. 80 // arrows and bubbles and don't allow navigate.
78 BOOL canNavigate_; 81 BOOL canNavigate_;
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after
203 // Animate selection bubbles dpending on distance. 206 // Animate selection bubbles dpending on distance.
204 [UIView beginAnimations:@"transform" context:NULL]; 207 [UIView beginAnimations:@"transform" context:NULL];
205 [UIView setAnimationDuration:kSelectionSnappingAnimationDuration]; 208 [UIView setAnimationDuration:kSelectionSnappingAnimationDuration];
206 if (distance < (width * kSwipeThreshold)) { 209 if (distance < (width * kSwipeThreshold)) {
207 // Scale selection down. 210 // Scale selection down.
208 selectionCircleLayer_.transform = 211 selectionCircleLayer_.transform =
209 CATransform3DMakeScale(kSelectionDownScale, kSelectionDownScale, 1); 212 CATransform3DMakeScale(kSelectionDownScale, kSelectionDownScale, 1);
210 selectionCircleLayer_.opacity = 0; 213 selectionCircleLayer_.opacity = 0;
211 [arrowView_ setAlpha:MapValueToRange({0, 64}, {0, 1}, distance)]; 214 [arrowView_ setAlpha:MapValueToRange({0, 64}, {0, 1}, distance)];
212 [arrowView_ setTintColor:[UIColor whiteColor]]; 215 [arrowView_ setTintColor:[UIColor whiteColor]];
216 thresholdTriggered_ = NO;
213 } else { 217 } else {
214 selectionCircleLayer_.transform = CATransform3DMakeScale(1, 1, 1); 218 selectionCircleLayer_.transform = CATransform3DMakeScale(1, 1, 1);
215 selectionCircleLayer_.opacity = 0.75; 219 selectionCircleLayer_.opacity = 0.75;
216 [arrowView_ setAlpha:1]; 220 [arrowView_ setAlpha:1];
217 [arrowView_ setTintColor:self.backgroundColor]; 221 [arrowView_ setTintColor:self.backgroundColor];
222 // Trigger a small haptic blip when exceeding the threshold and mark
223 // such that only one blip gets triggered.
224 if (!thresholdTriggered_) {
225 TriggerHapticFeedbackForSelectionChange();
226 thresholdTriggered_ = YES;
227 }
218 } 228 }
219 [UIView commitAnimations]; 229 [UIView commitAnimations];
220 } 230 }
221 231
222 - (void)explodeSelection:(void (^)(void))block { 232 - (void)explodeSelection:(void (^)(void))block {
223 [CATransaction begin]; 233 [CATransaction begin];
224 [CATransaction setCompletionBlock:^{ 234 [CATransaction setCompletionBlock:^{
225 // Note that the animations below may complete at slightly different times 235 // Note that the animations below may complete at slightly different times
226 // resulting in frame(s) between animation completion and the transaction's 236 // resulting in frame(s) between animation completion and the transaction's
227 // completion handler that show the original state. To avoid this flicker, 237 // completion handler that show the original state. To avoid this flicker,
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 332
323 if (gesture.state == UIGestureRecognizerStateEnded || 333 if (gesture.state == UIGestureRecognizerStateEnded ||
324 gesture.state == UIGestureRecognizerStateCancelled || 334 gesture.state == UIGestureRecognizerStateCancelled ||
325 gesture.state == UIGestureRecognizerStateFailed) { 335 gesture.state == UIGestureRecognizerStateFailed) {
326 CGFloat threshold = width * kSwipeThreshold; 336 CGFloat threshold = width * kSwipeThreshold;
327 CGFloat finalDistance = distance + velocityOffset; 337 CGFloat finalDistance = distance + velocityOffset;
328 // Ensure the actual distance traveled has met the minimum arrow threshold 338 // Ensure the actual distance traveled has met the minimum arrow threshold
329 // and that the distance including expected velocity is over |threshold|. 339 // and that the distance including expected velocity is over |threshold|.
330 if (distance > kArrowThreshold && finalDistance > threshold && 340 if (distance > kArrowThreshold && finalDistance > threshold &&
331 canNavigate_ && gesture.state == UIGestureRecognizerStateEnded) { 341 canNavigate_ && gesture.state == UIGestureRecognizerStateEnded) {
342 TriggerHapticFeedbackForAction();
343
332 // Speed up the animation for higher velocity swipes. 344 // Speed up the animation for higher velocity swipes.
333 CGFloat animationTime = MapValueToRange( 345 CGFloat animationTime = MapValueToRange(
334 {threshold, width}, 346 {threshold, width},
335 {kSelectionAnimationDuration, kSelectionAnimationDuration / 2}, 347 {kSelectionAnimationDuration, kSelectionAnimationDuration / 2},
336 finalDistance); 348 finalDistance);
337 [self animateTargetViewCompleted:YES 349 [self animateTargetViewCompleted:YES
338 withDirection:gesture.direction 350 withDirection:gesture.direction
339 withDuration:animationTime]; 351 withDuration:animationTime];
340 [self explodeSelection:onOverThresholdCompletion]; 352 [self explodeSelection:onOverThresholdCompletion];
341 if (IsSwipingForward(gesture.direction)) { 353 if (IsSwipingForward(gesture.direction)) {
342 base::RecordAction(base::UserMetricsAction( 354 base::RecordAction(base::UserMetricsAction(
343 "MobileEdgeSwipeNavigationForwardCompleted")); 355 "MobileEdgeSwipeNavigationForwardCompleted"));
344 } else { 356 } else {
345 base::RecordAction( 357 base::RecordAction(
346 base::UserMetricsAction("MobileEdgeSwipeNavigationBackCompleted")); 358 base::UserMetricsAction("MobileEdgeSwipeNavigationBackCompleted"));
347 } 359 }
348 } else { 360 } else {
349 [self animateTargetViewCompleted:NO 361 [self animateTargetViewCompleted:NO
350 withDirection:gesture.direction 362 withDirection:gesture.direction
351 withDuration:0.1]; 363 withDuration:0.1];
352 onUnderThresholdCompletion(); 364 onUnderThresholdCompletion();
353 if (IsSwipingForward(gesture.direction)) { 365 if (IsSwipingForward(gesture.direction)) {
354 base::RecordAction(base::UserMetricsAction( 366 base::RecordAction(base::UserMetricsAction(
355 "MobileEdgeSwipeNavigationForwardCancelled")); 367 "MobileEdgeSwipeNavigationForwardCancelled"));
356 } else { 368 } else {
357 base::RecordAction( 369 base::RecordAction(
358 base::UserMetricsAction("MobileEdgeSwipeNavigationBackCancelled")); 370 base::UserMetricsAction("MobileEdgeSwipeNavigationBackCancelled"));
359 } 371 }
360 } 372 }
373 thresholdTriggered_ = NO;
361 } 374 }
362 } 375 }
363 376
364 - (void)animateTargetViewCompleted:(BOOL)completed 377 - (void)animateTargetViewCompleted:(BOOL)completed
365 withDirection:(UISwipeGestureRecognizerDirection)direction 378 withDirection:(UISwipeGestureRecognizerDirection)direction
366 withDuration:(CGFloat)duration { 379 withDuration:(CGFloat)duration {
367 void (^animationBlock)(void) = ^{ 380 void (^animationBlock)(void) = ^{
368 CGRect targetFrame = self.targetView.frame; 381 CGRect targetFrame = self.targetView.frame;
369 CGRect frame = self.frame; 382 CGRect frame = self.frame;
370 CGFloat width = CGRectGetWidth(self.targetView.bounds); 383 CGFloat width = CGRectGetWidth(self.targetView.bounds);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
415 selectionCircleLayer.opacity = 0; 428 selectionCircleLayer.opacity = 0;
416 selectionCircleLayer.transform = 429 selectionCircleLayer.transform =
417 CATransform3DMakeScale(kSelectionDownScale, kSelectionDownScale, 1); 430 CATransform3DMakeScale(kSelectionDownScale, kSelectionDownScale, 1);
418 selectionCircleLayer.path = 431 selectionCircleLayer.path =
419 [[UIBezierPath bezierPathWithOvalInRect:bounds] CGPath]; 432 [[UIBezierPath bezierPathWithOvalInRect:bounds] CGPath];
420 433
421 return selectionCircleLayer; 434 return selectionCircleLayer;
422 } 435 }
423 436
424 @end 437 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.mm ('k') | ios/chrome/browser/ui/uikit_ui_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698