| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 |
| OLD | NEW |