| 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/overscroll_actions/overscroll_actions_controller.
h" | 5 #import "ios/chrome/browser/ui/overscroll_actions/overscroll_actions_controller.
h" |
| 6 | 6 |
| 7 #import <QuartzCore/QuartzCore.h> | 7 #import <QuartzCore/QuartzCore.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 87 CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) { | 87 CGFloat Clamp(CGFloat value, CGFloat min, CGFloat max) { |
| 88 DCHECK(min < max); | 88 DCHECK(min < max); |
| 89 if (value < min) | 89 if (value < min) |
| 90 return min; | 90 return min; |
| 91 if (value > max) | 91 if (value > max) |
| 92 return max; | 92 return max; |
| 93 return value; | 93 return value; |
| 94 } | 94 } |
| 95 } // namespace | 95 } // namespace |
| 96 | 96 |
| 97 namespace ios_internal { | 97 NSString* const kOverscrollActionsWillStart = @"OverscrollActionsWillStart"; |
| 98 NSString* const kOverscollActionsWillStart = @"OverscollActionsWillStart"; | 98 NSString* const kOverscrollActionsDidEnd = @"OverscrollActionsDidStop"; |
| 99 NSString* const kOverscollActionsDidEnd = @"OverscollActionsDidStop"; | |
| 100 } // namespace ios_internal | |
| 101 | 99 |
| 102 // This protocol describes the subset of methods used between the | 100 // This protocol describes the subset of methods used between the |
| 103 // CRWWebViewScrollViewProxy and the UIWebView. | 101 // CRWWebViewScrollViewProxy and the UIWebView. |
| 104 @protocol OverscrollActionsScrollView<NSObject> | 102 @protocol OverscrollActionsScrollView<NSObject> |
| 105 | 103 |
| 106 @property(nonatomic, assign) UIEdgeInsets contentInset; | 104 @property(nonatomic, assign) UIEdgeInsets contentInset; |
| 107 @property(nonatomic, assign) CGPoint contentOffset; | 105 @property(nonatomic, assign) CGPoint contentOffset; |
| 108 @property(nonatomic, assign) UIEdgeInsets scrollIndicatorInsets; | 106 @property(nonatomic, assign) UIEdgeInsets scrollIndicatorInsets; |
| 109 @property(nonatomic, readonly) UIPanGestureRecognizer* panGestureRecognizer; | 107 @property(nonatomic, readonly) UIPanGestureRecognizer* panGestureRecognizer; |
| 110 @property(nonatomic, readonly) BOOL isZooming; | 108 @property(nonatomic, readonly) BOOL isZooming; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 @property(nonatomic, readonly) CGFloat initialContentInset; | 174 @property(nonatomic, readonly) CGFloat initialContentInset; |
| 177 // Initial top inset for the header. | 175 // Initial top inset for the header. |
| 178 // This property is set from the delegate headerInset and cached on first | 176 // This property is set from the delegate headerInset and cached on first |
| 179 // call. The cached value is reset when the webview proxy is set. | 177 // call. The cached value is reset when the webview proxy is set. |
| 180 @property(nonatomic, readonly) CGFloat initialHeaderInset; | 178 @property(nonatomic, readonly) CGFloat initialHeaderInset; |
| 181 // Initial height of the header view. | 179 // Initial height of the header view. |
| 182 // This property is set from the delegate headerHeight and cached on first | 180 // This property is set from the delegate headerHeight and cached on first |
| 183 // call. The cached value is reset when the webview proxy is set. | 181 // call. The cached value is reset when the webview proxy is set. |
| 184 @property(nonatomic, readonly) CGFloat initialHeaderHeight; | 182 @property(nonatomic, readonly) CGFloat initialHeaderHeight; |
| 185 // Redefined to be read-write. | 183 // Redefined to be read-write. |
| 186 @property(nonatomic, assign, readwrite) | 184 @property(nonatomic, assign, readwrite) OverscrollState overscrollState; |
| 187 ios_internal::OverscrollState overscrollState; | |
| 188 // Point where the horizontal gesture started when the state of the | 185 // Point where the horizontal gesture started when the state of the |
| 189 // overscroll controller is in OverscrollStateActionReady. | 186 // overscroll controller is in OverscrollStateActionReady. |
| 190 @property(nonatomic, assign) CGPoint panPointScreenOrigin; | 187 @property(nonatomic, assign) CGPoint panPointScreenOrigin; |
| 191 // Pan gesture recognizer used to track horizontal touches. | 188 // Pan gesture recognizer used to track horizontal touches. |
| 192 @property(nonatomic, retain) UIPanGestureRecognizer* panGestureRecognizer; | 189 @property(nonatomic, retain) UIPanGestureRecognizer* panGestureRecognizer; |
| 193 | 190 |
| 194 // Registers notifications to lock the overscroll actions on certain UI states. | 191 // Registers notifications to lock the overscroll actions on certain UI states. |
| 195 - (void)registerNotifications; | 192 - (void)registerNotifications; |
| 196 // Setup/tearDown methods are used to register values when the delegate is set. | 193 // Setup/tearDown methods are used to register values when the delegate is set. |
| 197 - (void)tearDown; | 194 - (void)tearDown; |
| 198 - (void)setup; | 195 - (void)setup; |
| 199 // Resets scroll view's top content inset to |self.initialContentInset|. | 196 // Resets scroll view's top content inset to |self.initialContentInset|. |
| 200 - (void)resetScrollViewTopContentInset; | 197 - (void)resetScrollViewTopContentInset; |
| 201 // Access the headerView from the delegate. | 198 // Access the headerView from the delegate. |
| 202 - (UIView<RelaxedBoundsConstraintsHitTestSupport>*)headerView; | 199 - (UIView<RelaxedBoundsConstraintsHitTestSupport>*)headerView; |
| 203 // Locking/unlocking methods used to disable/enable the overscroll actions | 200 // Locking/unlocking methods used to disable/enable the overscroll actions |
| 204 // with a reference count. | 201 // with a reference count. |
| 205 - (void)incrementOverscrollActionLockForNotification: | 202 - (void)incrementOverscrollActionLockForNotification: |
| 206 (NSNotification*)notification; | 203 (NSNotification*)notification; |
| 207 - (void)decrementOverscrollActionLockForNotification: | 204 - (void)decrementOverscrollActionLockForNotification: |
| 208 (NSNotification*)notification; | 205 (NSNotification*)notification; |
| 209 // Indicates whether the overscroll action is allowed. | 206 // Indicates whether the overscroll action is allowed. |
| 210 - (BOOL)isOverscrollActionEnabled; | 207 - (BOOL)isOverscrollActionEnabled; |
| 211 // Triggers a call to delegate if an action has been triggered. | 208 // Triggers a call to delegate if an action has been triggered. |
| 212 - (void)triggerActionIfNeeded; | 209 - (void)triggerActionIfNeeded; |
| 213 // Performs work based on overscroll action state changes. | 210 // Performs work based on overscroll action state changes. |
| 214 - (void)onOverscrollStateChangeWithPreviousState: | 211 - (void)onOverscrollStateChangeWithPreviousState: |
| 215 (ios_internal::OverscrollState)previousOverscrollState; | 212 (OverscrollState)previousOverscrollState; |
| 216 // Disables all interactions on the webview except pan. | 213 // Disables all interactions on the webview except pan. |
| 217 - (void)setWebViewInteractionEnabled:(BOOL)enabled; | 214 - (void)setWebViewInteractionEnabled:(BOOL)enabled; |
| 218 // Bounce dynamic animations methods. | 215 // Bounce dynamic animations methods. |
| 219 // Starts the bounce animation with an initial velocity. | 216 // Starts the bounce animation with an initial velocity. |
| 220 - (void)startBounceWithInitialVelocity:(CGPoint)velocity; | 217 - (void)startBounceWithInitialVelocity:(CGPoint)velocity; |
| 221 // Stops bounce animation. | 218 // Stops bounce animation. |
| 222 - (void)stopBounce; | 219 - (void)stopBounce; |
| 223 // Called from the display link to update the bounce dynamic animation. | 220 // Called from the display link to update the bounce dynamic animation. |
| 224 - (void)updateBounce; | 221 - (void)updateBounce; |
| 225 // Applies bounce state to the scroll view. | 222 // Applies bounce state to the scroll view. |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 287 [self tearDown]; | 284 [self tearDown]; |
| 288 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 285 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 289 [self setWebViewInteractionEnabled:YES]; | 286 [self setWebViewInteractionEnabled:YES]; |
| 290 _delegate = nil; | 287 _delegate = nil; |
| 291 _webViewProxy.reset(); | 288 _webViewProxy.reset(); |
| 292 [_webViewScrollViewProxy removeObserver:self]; | 289 [_webViewScrollViewProxy removeObserver:self]; |
| 293 _webViewScrollViewProxy.reset(); | 290 _webViewScrollViewProxy.reset(); |
| 294 } | 291 } |
| 295 | 292 |
| 296 - (void)clear { | 293 - (void)clear { |
| 297 self.overscrollState = ios_internal::OverscrollState::NO_PULL_STARTED; | 294 self.overscrollState = OverscrollState::NO_PULL_STARTED; |
| 298 } | 295 } |
| 299 | 296 |
| 300 - (void)enableOverscrollActions { | 297 - (void)enableOverscrollActions { |
| 301 _isOverscrollActionsDisabledForLoading = NO; | 298 _isOverscrollActionsDisabledForLoading = NO; |
| 302 [self setup]; | 299 [self setup]; |
| 303 } | 300 } |
| 304 | 301 |
| 305 - (void)disableOverscrollActions { | 302 - (void)disableOverscrollActions { |
| 306 _isOverscrollActionsDisabledForLoading = YES; | 303 _isOverscrollActionsDisabledForLoading = YES; |
| 307 [self tearDown]; | 304 [self tearDown]; |
| 308 } | 305 } |
| 309 | 306 |
| 310 - (void)setStyle:(ios_internal::OverscrollStyle)style { | 307 - (void)setStyle:(OverscrollStyle)style { |
| 311 [self.overscrollActionView setStyle:style]; | 308 [self.overscrollActionView setStyle:style]; |
| 312 } | 309 } |
| 313 | 310 |
| 314 #pragma mark - webViewScrollView and UIScrollView delegates implementations | 311 #pragma mark - webViewScrollView and UIScrollView delegates implementations |
| 315 | 312 |
| 316 - (void)scrollViewDidScroll { | 313 - (void)scrollViewDidScroll { |
| 317 if (!_forceStateUpdate && (![self isOverscrollActionEnabled] || | 314 if (!_forceStateUpdate && (![self isOverscrollActionEnabled] || |
| 318 _performingScrollViewIndependentAnimation)) | 315 _performingScrollViewIndependentAnimation)) |
| 319 return; | 316 return; |
| 320 | 317 |
| 321 const UIEdgeInsets insets = | 318 const UIEdgeInsets insets = |
| 322 UIEdgeInsetsMake(-[self scrollView].contentOffset.y, 0, 0, 0); | 319 UIEdgeInsetsMake(-[self scrollView].contentOffset.y, 0, 0, 0); |
| 323 // Start pulling (on top). | 320 // Start pulling (on top). |
| 324 CGFloat contentOffsetFromTheTop = [self scrollView].contentOffset.y; | 321 CGFloat contentOffsetFromTheTop = [self scrollView].contentOffset.y; |
| 325 if (![_webViewProxy shouldUseInsetForTopPadding]) { | 322 if (![_webViewProxy shouldUseInsetForTopPadding]) { |
| 326 // Content offset is shifted for WKWebView when the web view's | 323 // Content offset is shifted for WKWebView when the web view's |
| 327 // |shouldUseInsetForTopPadding| is NO, to workaround bug with | 324 // |shouldUseInsetForTopPadding| is NO, to workaround bug with |
| 328 // UIScollView.contentInset (rdar://23584409). | 325 // UIScollView.contentInset (rdar://23584409). |
| 329 contentOffsetFromTheTop -= [_webViewProxy topContentPadding]; | 326 contentOffsetFromTheTop -= [_webViewProxy topContentPadding]; |
| 330 } | 327 } |
| 331 CGFloat contentOffsetFromExpandedHeader = | 328 CGFloat contentOffsetFromExpandedHeader = |
| 332 contentOffsetFromTheTop + self.initialHeaderInset; | 329 contentOffsetFromTheTop + self.initialHeaderInset; |
| 333 if (contentOffsetFromExpandedHeader >= 0) { | 330 if (contentOffsetFromExpandedHeader >= 0) { |
| 334 // Record initial content offset and dispatch delegate on state change. | 331 // Record initial content offset and dispatch delegate on state change. |
| 335 self.overscrollState = ios_internal::OverscrollState::NO_PULL_STARTED; | 332 self.overscrollState = OverscrollState::NO_PULL_STARTED; |
| 336 } else { | 333 } else { |
| 337 if (contentOffsetFromExpandedHeader < -kHeaderMaxExpansionThreshold) { | 334 if (contentOffsetFromExpandedHeader < -kHeaderMaxExpansionThreshold) { |
| 338 self.overscrollState = ios_internal::OverscrollState::ACTION_READY; | 335 self.overscrollState = OverscrollState::ACTION_READY; |
| 339 [self scrollView].scrollIndicatorInsets = insets; | 336 [self scrollView].scrollIndicatorInsets = insets; |
| 340 } else { | 337 } else { |
| 341 // Set the contentInset to remove the bounce that would fight with drag. | 338 // Set the contentInset to remove the bounce that would fight with drag. |
| 342 [self setScrollViewContentInset:insets]; | 339 [self setScrollViewContentInset:insets]; |
| 343 [self scrollView].scrollIndicatorInsets = insets; | 340 [self scrollView].scrollIndicatorInsets = insets; |
| 344 self.overscrollState = ios_internal::OverscrollState::STARTED_PULLING; | 341 self.overscrollState = OverscrollState::STARTED_PULLING; |
| 345 } | 342 } |
| 346 [self updateWithVerticalOffset:-contentOffsetFromExpandedHeader]; | 343 [self updateWithVerticalOffset:-contentOffsetFromExpandedHeader]; |
| 347 } | 344 } |
| 348 } | 345 } |
| 349 | 346 |
| 350 - (void)scrollViewWillBeginDragging { | 347 - (void)scrollViewWillBeginDragging { |
| 351 [self stopBounce]; | 348 [self stopBounce]; |
| 352 _allowPullingActions = NO; | 349 _allowPullingActions = NO; |
| 353 _didTransitionToActionReady = NO; | 350 _didTransitionToActionReady = NO; |
| 354 [self.overscrollActionView pullStarted]; | 351 [self.overscrollActionView pullStarted]; |
| 355 if (!_performingScrollViewIndependentAnimation) | 352 if (!_performingScrollViewIndependentAnimation) |
| 356 _allowPullingActions = [self isOverscrollActionsAllowed]; | 353 _allowPullingActions = [self isOverscrollActionsAllowed]; |
| 357 _lastScrollBeginTime = CACurrentMediaTime(); | 354 _lastScrollBeginTime = CACurrentMediaTime(); |
| 358 } | 355 } |
| 359 | 356 |
| 360 - (BOOL)isOverscrollActionsAllowed { | 357 - (BOOL)isOverscrollActionsAllowed { |
| 361 const BOOL isZooming = [[self scrollView] isZooming]; | 358 const BOOL isZooming = [[self scrollView] isZooming]; |
| 362 // Check that the scrollview is scrolled to top. | 359 // Check that the scrollview is scrolled to top. |
| 363 const BOOL isScrolledToTop = fabs([[self scrollView] contentOffset].y + | 360 const BOOL isScrolledToTop = fabs([[self scrollView] contentOffset].y + |
| 364 [[self scrollView] contentInset].top) <= | 361 [[self scrollView] contentInset].top) <= |
| 365 kScrolledToTopToleranceInPoint; | 362 kScrolledToTopToleranceInPoint; |
| 366 // Check that the user is not quickly scrolling the view repeatedly. | 363 // Check that the user is not quickly scrolling the view repeatedly. |
| 367 const BOOL isMinimumTimeBetweenScrollRespected = | 364 const BOOL isMinimumTimeBetweenScrollRespected = |
| 368 (CACurrentMediaTime() - _lastScrollBeginTime) >= | 365 (CACurrentMediaTime() - _lastScrollBeginTime) >= |
| 369 kMinimumDurationBetweenScrollingInSeconds; | 366 kMinimumDurationBetweenScrollingInSeconds; |
| 370 // Finally check that the delegate allow overscroll actions. | 367 // Finally check that the delegate allow overscroll actions. |
| 371 const BOOL delegateAllowOverscrollActions = | 368 const BOOL delegateAllowOverscrollActions = |
| 372 [self.delegate shouldAllowOverscrollActions]; | 369 [self.delegate shouldAllowOverscrollActions]; |
| 373 const BOOL isCurrentlyProcessingOverscroll = | 370 const BOOL isCurrentlyProcessingOverscroll = |
| 374 self.overscrollState != ios_internal::OverscrollState::NO_PULL_STARTED; | 371 self.overscrollState != OverscrollState::NO_PULL_STARTED; |
| 375 return isCurrentlyProcessingOverscroll || | 372 return isCurrentlyProcessingOverscroll || |
| 376 (isScrolledToTop && isMinimumTimeBetweenScrollRespected && | 373 (isScrolledToTop && isMinimumTimeBetweenScrollRespected && |
| 377 delegateAllowOverscrollActions && !isZooming); | 374 delegateAllowOverscrollActions && !isZooming); |
| 378 } | 375 } |
| 379 | 376 |
| 380 - (void)scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate | 377 - (void)scrollViewDidEndDraggingWillDecelerate:(BOOL)decelerate |
| 381 contentOffset:(CGPoint)contentOffset { | 378 contentOffset:(CGPoint)contentOffset { |
| 382 // Content is now hidden behind toolbar, make sure that contentInset is | 379 // Content is now hidden behind toolbar, make sure that contentInset is |
| 383 // restored to initial value. | 380 // restored to initial value. |
| 384 if (contentOffset.y >= 0 || | 381 if (contentOffset.y >= 0 || |
| 385 self.overscrollState == ios_internal::OverscrollState::NO_PULL_STARTED) { | 382 self.overscrollState == OverscrollState::NO_PULL_STARTED) { |
| 386 [self resetScrollViewTopContentInset]; | 383 [self resetScrollViewTopContentInset]; |
| 387 } | 384 } |
| 388 | 385 |
| 389 [self triggerActionIfNeeded]; | 386 [self triggerActionIfNeeded]; |
| 390 _allowPullingActions = NO; | 387 _allowPullingActions = NO; |
| 391 } | 388 } |
| 392 | 389 |
| 393 - (void)scrollViewWillEndDraggingWithVelocity:(CGPoint)velocity | 390 - (void)scrollViewWillEndDraggingWithVelocity:(CGPoint)velocity |
| 394 targetContentOffset: | 391 targetContentOffset: |
| 395 (inout CGPoint*)targetContentOffset { | 392 (inout CGPoint*)targetContentOffset { |
| 396 if (![self isOverscrollActionEnabled]) | 393 if (![self isOverscrollActionEnabled]) |
| 397 return; | 394 return; |
| 398 | 395 |
| 399 if (self.overscrollState != ios_internal::OverscrollState::NO_PULL_STARTED) { | 396 if (self.overscrollState != OverscrollState::NO_PULL_STARTED) { |
| 400 *targetContentOffset = [[self scrollView] contentOffset]; | 397 *targetContentOffset = [[self scrollView] contentOffset]; |
| 401 [self startBounceWithInitialVelocity:velocity]; | 398 [self startBounceWithInitialVelocity:velocity]; |
| 402 } | 399 } |
| 403 } | 400 } |
| 404 | 401 |
| 405 - (void)webViewScrollViewProxyDidSetScrollView: | 402 - (void)webViewScrollViewProxyDidSetScrollView: |
| 406 (CRWWebViewScrollViewProxy*)webViewScrollViewProxy { | 403 (CRWWebViewScrollViewProxy*)webViewScrollViewProxy { |
| 407 [self setup]; | 404 [self setup]; |
| 408 } | 405 } |
| 409 | 406 |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 469 #pragma mark - Pan gesture recognizer handling | 466 #pragma mark - Pan gesture recognizer handling |
| 470 | 467 |
| 471 - (void)panGesture:(UIPanGestureRecognizer*)gesture { | 468 - (void)panGesture:(UIPanGestureRecognizer*)gesture { |
| 472 if (gesture.state == UIGestureRecognizerStateBegan) { | 469 if (gesture.state == UIGestureRecognizerStateBegan) { |
| 473 [self setWebViewInteractionEnabled:NO]; | 470 [self setWebViewInteractionEnabled:NO]; |
| 474 } else if (gesture.state == UIGestureRecognizerStateEnded || | 471 } else if (gesture.state == UIGestureRecognizerStateEnded || |
| 475 gesture.state == UIGestureRecognizerStateCancelled) { | 472 gesture.state == UIGestureRecognizerStateCancelled) { |
| 476 [self setWebViewInteractionEnabled:YES]; | 473 [self setWebViewInteractionEnabled:YES]; |
| 477 } | 474 } |
| 478 const CGPoint panPointScreen = [gesture locationInView:nil]; | 475 const CGPoint panPointScreen = [gesture locationInView:nil]; |
| 479 if (self.overscrollState == ios_internal::OverscrollState::ACTION_READY) { | 476 if (self.overscrollState == OverscrollState::ACTION_READY) { |
| 480 const CGFloat direction = UseRTLLayout() ? -1 : 1; | 477 const CGFloat direction = UseRTLLayout() ? -1 : 1; |
| 481 const CGFloat xOffset = direction * | 478 const CGFloat xOffset = direction * |
| 482 (panPointScreen.x - self.panPointScreenOrigin.x) / | 479 (panPointScreen.x - self.panPointScreenOrigin.x) / |
| 483 kHorizontalPanDistance; | 480 kHorizontalPanDistance; |
| 484 | 481 |
| 485 [self.overscrollActionView updateWithHorizontalOffset:xOffset]; | 482 [self.overscrollActionView updateWithHorizontalOffset:xOffset]; |
| 486 } | 483 } |
| 487 } | 484 } |
| 488 | 485 |
| 489 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer | 486 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer |
| (...skipping 18 matching lines...) Expand all Loading... |
| 508 | 505 |
| 509 - (void)webControllerWillClose:(CRWWebController*)webController { | 506 - (void)webControllerWillClose:(CRWWebController*)webController { |
| 510 [self disableOverscrollActions]; | 507 [self disableOverscrollActions]; |
| 511 [_webViewScrollViewProxy removeObserver:self]; | 508 [_webViewScrollViewProxy removeObserver:self]; |
| 512 _webViewScrollViewProxy.reset(); | 509 _webViewScrollViewProxy.reset(); |
| 513 [webController removeObserver:self]; | 510 [webController removeObserver:self]; |
| 514 } | 511 } |
| 515 | 512 |
| 516 #pragma mark - Private | 513 #pragma mark - Private |
| 517 | 514 |
| 518 - (void)recordMetricForTriggeredAction:(ios_internal::OverscrollAction)action { | 515 - (void)recordMetricForTriggeredAction:(OverscrollAction)action { |
| 519 switch (action) { | 516 switch (action) { |
| 520 case ios_internal::OverscrollAction::NONE: | 517 case OverscrollAction::NONE: |
| 521 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, | 518 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, |
| 522 OVERSCROLL_ACTION_CANCELED, | 519 OVERSCROLL_ACTION_CANCELED, |
| 523 OVERSCROLL_ACTION_COUNT); | 520 OVERSCROLL_ACTION_COUNT); |
| 524 break; | 521 break; |
| 525 case ios_internal::OverscrollAction::NEW_TAB: | 522 case OverscrollAction::NEW_TAB: |
| 526 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, | 523 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, |
| 527 OVERSCROLL_ACTION_NEW_TAB, | 524 OVERSCROLL_ACTION_NEW_TAB, |
| 528 OVERSCROLL_ACTION_COUNT); | 525 OVERSCROLL_ACTION_COUNT); |
| 529 break; | 526 break; |
| 530 case ios_internal::OverscrollAction::REFRESH: | 527 case OverscrollAction::REFRESH: |
| 531 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, | 528 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, |
| 532 OVERSCROLL_ACTION_REFRESH, | 529 OVERSCROLL_ACTION_REFRESH, |
| 533 OVERSCROLL_ACTION_COUNT); | 530 OVERSCROLL_ACTION_COUNT); |
| 534 break; | 531 break; |
| 535 case ios_internal::OverscrollAction::CLOSE_TAB: | 532 case OverscrollAction::CLOSE_TAB: |
| 536 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, | 533 UMA_HISTOGRAM_ENUMERATION(kOverscrollActionsHistogram, |
| 537 OVERSCROLL_ACTION_CLOSE_TAB, | 534 OVERSCROLL_ACTION_CLOSE_TAB, |
| 538 OVERSCROLL_ACTION_COUNT); | 535 OVERSCROLL_ACTION_COUNT); |
| 539 break; | 536 break; |
| 540 } | 537 } |
| 541 } | 538 } |
| 542 | 539 |
| 543 - (void)registerNotifications { | 540 - (void)registerNotifications { |
| 544 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | 541 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 545 for (NSString* counterpartNotificationName in _lockNotificationsCounterparts | 542 for (NSString* counterpartNotificationName in _lockNotificationsCounterparts |
| (...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 612 NSString* counterpartName = | 609 NSString* counterpartName = |
| 613 [_lockNotificationsCounterparts objectForKey:notif.name]; | 610 [_lockNotificationsCounterparts objectForKey:notif.name]; |
| 614 if ([_lockIncrementNotifications containsObject:counterpartName]) { | 611 if ([_lockIncrementNotifications containsObject:counterpartName]) { |
| 615 [_lockIncrementNotifications removeObject:counterpartName]; | 612 [_lockIncrementNotifications removeObject:counterpartName]; |
| 616 if (_overscrollActionLock > 0) | 613 if (_overscrollActionLock > 0) |
| 617 --_overscrollActionLock; | 614 --_overscrollActionLock; |
| 618 } | 615 } |
| 619 } | 616 } |
| 620 | 617 |
| 621 - (void)deviceOrientationDidChange { | 618 - (void)deviceOrientationDidChange { |
| 622 if (self.overscrollState == ios_internal::OverscrollState::NO_PULL_STARTED && | 619 if (self.overscrollState == OverscrollState::NO_PULL_STARTED && |
| 623 !_performingScrollViewIndependentAnimation) | 620 !_performingScrollViewIndependentAnimation) |
| 624 return; | 621 return; |
| 625 | 622 |
| 626 const UIDeviceOrientation deviceOrientation = | 623 const UIDeviceOrientation deviceOrientation = |
| 627 [[UIDevice currentDevice] orientation]; | 624 [[UIDevice currentDevice] orientation]; |
| 628 if (deviceOrientation != UIDeviceOrientationLandscapeRight && | 625 if (deviceOrientation != UIDeviceOrientationLandscapeRight && |
| 629 deviceOrientation != UIDeviceOrientationLandscapeLeft && | 626 deviceOrientation != UIDeviceOrientationLandscapeLeft && |
| 630 deviceOrientation != UIDeviceOrientationPortrait) { | 627 deviceOrientation != UIDeviceOrientationPortrait) { |
| 631 return; | 628 return; |
| 632 } | 629 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 644 } | 641 } |
| 645 | 642 |
| 646 - (BOOL)isOverscrollActionEnabled { | 643 - (BOOL)isOverscrollActionEnabled { |
| 647 return _overscrollActionLock == 0 && _allowPullingActions && | 644 return _overscrollActionLock == 0 && _allowPullingActions && |
| 648 !_isOverscrollActionsDisabledForLoading; | 645 !_isOverscrollActionsDisabledForLoading; |
| 649 } | 646 } |
| 650 | 647 |
| 651 - (void)triggerActionIfNeeded { | 648 - (void)triggerActionIfNeeded { |
| 652 if ([self isOverscrollActionEnabled]) { | 649 if ([self isOverscrollActionEnabled]) { |
| 653 const BOOL isOverscrollStateActionReady = | 650 const BOOL isOverscrollStateActionReady = |
| 654 self.overscrollState == ios_internal::OverscrollState::ACTION_READY; | 651 self.overscrollState == OverscrollState::ACTION_READY; |
| 655 const BOOL isOverscrollActionNone = | 652 const BOOL isOverscrollActionNone = |
| 656 self.overscrollActionView.selectedAction == | 653 self.overscrollActionView.selectedAction == OverscrollAction::NONE; |
| 657 ios_internal::OverscrollAction::NONE; | |
| 658 | 654 |
| 659 if ((!isOverscrollStateActionReady && _didTransitionToActionReady) || | 655 if ((!isOverscrollStateActionReady && _didTransitionToActionReady) || |
| 660 (isOverscrollStateActionReady && isOverscrollActionNone)) { | 656 (isOverscrollStateActionReady && isOverscrollActionNone)) { |
| 661 [self | 657 [self recordMetricForTriggeredAction:OverscrollAction::NONE]; |
| 662 recordMetricForTriggeredAction:ios_internal::OverscrollAction::NONE]; | |
| 663 } else if (isOverscrollStateActionReady && !isOverscrollActionNone) { | 658 } else if (isOverscrollStateActionReady && !isOverscrollActionNone) { |
| 664 if (CACurrentMediaTime() - _lastScrollBeginTime >= | 659 if (CACurrentMediaTime() - _lastScrollBeginTime >= |
| 665 kMinimumPullDurationToTriggerActionInSeconds) { | 660 kMinimumPullDurationToTriggerActionInSeconds) { |
| 666 _performingScrollViewIndependentAnimation = YES; | 661 _performingScrollViewIndependentAnimation = YES; |
| 667 [self setScrollViewContentInset:UIEdgeInsetsMake( | 662 [self setScrollViewContentInset:UIEdgeInsetsMake( |
| 668 self.initialContentInset, 0, 0, 0)]; | 663 self.initialContentInset, 0, 0, 0)]; |
| 669 CGPoint contentOffset = [[self scrollView] contentOffset]; | 664 CGPoint contentOffset = [[self scrollView] contentOffset]; |
| 670 contentOffset.y = -self.initialContentInset; | 665 contentOffset.y = -self.initialContentInset; |
| 671 [[self scrollView] setContentOffset:contentOffset animated:YES]; | 666 [[self scrollView] setContentOffset:contentOffset animated:YES]; |
| 672 [self.overscrollActionView displayActionAnimation]; | 667 [self.overscrollActionView displayActionAnimation]; |
| 673 dispatch_async(dispatch_get_main_queue(), ^{ | 668 dispatch_async(dispatch_get_main_queue(), ^{ |
| 674 [self recordMetricForTriggeredAction:self.overscrollActionView | 669 [self recordMetricForTriggeredAction:self.overscrollActionView |
| 675 .selectedAction]; | 670 .selectedAction]; |
| 676 [self.delegate overscrollActionsController:self | 671 [self.delegate overscrollActionsController:self |
| 677 didTriggerAction:self.overscrollActionView | 672 didTriggerAction:self.overscrollActionView |
| 678 .selectedAction]; | 673 .selectedAction]; |
| 679 }); | 674 }); |
| 680 } | 675 } |
| 681 } | 676 } |
| 682 } | 677 } |
| 683 } | 678 } |
| 684 | 679 |
| 685 - (void)setOverscrollState:(ios_internal::OverscrollState)overscrollState { | 680 - (void)setOverscrollState:(OverscrollState)overscrollState { |
| 686 if (_overscrollState != overscrollState) { | 681 if (_overscrollState != overscrollState) { |
| 687 const ios_internal::OverscrollState previousState = _overscrollState; | 682 const OverscrollState previousState = _overscrollState; |
| 688 _overscrollState = overscrollState; | 683 _overscrollState = overscrollState; |
| 689 [self onOverscrollStateChangeWithPreviousState:previousState]; | 684 [self onOverscrollStateChangeWithPreviousState:previousState]; |
| 690 } | 685 } |
| 691 } | 686 } |
| 692 | 687 |
| 693 - (void)onOverscrollStateChangeWithPreviousState: | 688 - (void)onOverscrollStateChangeWithPreviousState: |
| 694 (ios_internal::OverscrollState)previousOverscrollState { | 689 (OverscrollState)previousOverscrollState { |
| 695 [UIView beginAnimations:@"backgroundColor" context:NULL]; | 690 [UIView beginAnimations:@"backgroundColor" context:NULL]; |
| 696 switch (self.overscrollState) { | 691 switch (self.overscrollState) { |
| 697 case ios_internal::OverscrollState::NO_PULL_STARTED: { | 692 case OverscrollState::NO_PULL_STARTED: { |
| 698 UIView<RelaxedBoundsConstraintsHitTestSupport>* headerView = | 693 UIView<RelaxedBoundsConstraintsHitTestSupport>* headerView = |
| 699 [self headerView]; | 694 [self headerView]; |
| 700 if ([headerView | 695 if ([headerView |
| 701 respondsToSelector:@selector(setHitTestBoundsContraintRelaxed:)]) | 696 respondsToSelector:@selector(setHitTestBoundsContraintRelaxed:)]) |
| 702 [headerView setHitTestBoundsContraintRelaxed:NO]; | 697 [headerView setHitTestBoundsContraintRelaxed:NO]; |
| 703 [self.overscrollActionView removeFromSuperview]; | 698 [self.overscrollActionView removeFromSuperview]; |
| 704 SetViewFrameHeight( | 699 SetViewFrameHeight( |
| 705 self.overscrollActionView, | 700 self.overscrollActionView, |
| 706 self.initialContentInset + | 701 self.initialContentInset + |
| 707 [UIApplication sharedApplication].statusBarFrame.size.height); | 702 [UIApplication sharedApplication].statusBarFrame.size.height); |
| 708 self.panPointScreenOrigin = CGPointZero; | 703 self.panPointScreenOrigin = CGPointZero; |
| 709 [[NSNotificationCenter defaultCenter] | 704 [[NSNotificationCenter defaultCenter] |
| 710 postNotificationName:ios_internal::kOverscollActionsDidEnd | 705 postNotificationName:kOverscrollActionsDidEnd |
| 711 object:self]; | 706 object:self]; |
| 712 } break; | 707 } break; |
| 713 case ios_internal::OverscrollState::STARTED_PULLING: { | 708 case OverscrollState::STARTED_PULLING: { |
| 714 if (!self.overscrollActionView.superview) { | 709 if (!self.overscrollActionView.superview) { |
| 715 if (previousOverscrollState == | 710 if (previousOverscrollState == OverscrollState::NO_PULL_STARTED) { |
| 716 ios_internal::OverscrollState::NO_PULL_STARTED) { | |
| 717 UIView* view = [self.delegate toolbarSnapshotView]; | 711 UIView* view = [self.delegate toolbarSnapshotView]; |
| 718 [self.overscrollActionView addSnapshotView:view]; | 712 [self.overscrollActionView addSnapshotView:view]; |
| 719 [[NSNotificationCenter defaultCenter] | 713 [[NSNotificationCenter defaultCenter] |
| 720 postNotificationName:ios_internal::kOverscollActionsWillStart | 714 postNotificationName:kOverscrollActionsWillStart |
| 721 object:self]; | 715 object:self]; |
| 722 } | 716 } |
| 723 [CATransaction begin]; | 717 [CATransaction begin]; |
| 724 [CATransaction setDisableActions:YES]; | 718 [CATransaction setDisableActions:YES]; |
| 725 self.overscrollActionView.backgroundView.alpha = 1; | 719 self.overscrollActionView.backgroundView.alpha = 1; |
| 726 [self.overscrollActionView updateWithVerticalOffset:0]; | 720 [self.overscrollActionView updateWithVerticalOffset:0]; |
| 727 [self.overscrollActionView updateWithHorizontalOffset:0]; | 721 [self.overscrollActionView updateWithHorizontalOffset:0]; |
| 728 self.overscrollActionView.frame = [self headerView].bounds; | 722 self.overscrollActionView.frame = [self headerView].bounds; |
| 729 DCHECK([self headerView]); | 723 DCHECK([self headerView]); |
| 730 UIView<RelaxedBoundsConstraintsHitTestSupport>* headerView = | 724 UIView<RelaxedBoundsConstraintsHitTestSupport>* headerView = |
| 731 [self headerView]; | 725 [self headerView]; |
| 732 if ([headerView | 726 if ([headerView |
| 733 respondsToSelector:@selector( | 727 respondsToSelector:@selector( |
| 734 setHitTestBoundsContraintRelaxed:)]) | 728 setHitTestBoundsContraintRelaxed:)]) |
| 735 [headerView setHitTestBoundsContraintRelaxed:YES]; | 729 [headerView setHitTestBoundsContraintRelaxed:YES]; |
| 736 [headerView addSubview:self.overscrollActionView]; | 730 [headerView addSubview:self.overscrollActionView]; |
| 737 [CATransaction commit]; | 731 [CATransaction commit]; |
| 738 } | 732 } |
| 739 } break; | 733 } break; |
| 740 case ios_internal::OverscrollState::ACTION_READY: { | 734 case OverscrollState::ACTION_READY: { |
| 741 _didTransitionToActionReady = YES; | 735 _didTransitionToActionReady = YES; |
| 742 if (CGPointEqualToPoint(self.panPointScreenOrigin, CGPointZero)) { | 736 if (CGPointEqualToPoint(self.panPointScreenOrigin, CGPointZero)) { |
| 743 CGPoint panPointScreen = [self.panGestureRecognizer locationInView:nil]; | 737 CGPoint panPointScreen = [self.panGestureRecognizer locationInView:nil]; |
| 744 self.panPointScreenOrigin = panPointScreen; | 738 self.panPointScreenOrigin = panPointScreen; |
| 745 } | 739 } |
| 746 } break; | 740 } break; |
| 747 } | 741 } |
| 748 [UIView commitAnimations]; | 742 [UIView commitAnimations]; |
| 749 } | 743 } |
| 750 | 744 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 807 #pragma mark - Bounce dynamic | 801 #pragma mark - Bounce dynamic |
| 808 | 802 |
| 809 - (void)startBounceWithInitialVelocity:(CGPoint)velocity { | 803 - (void)startBounceWithInitialVelocity:(CGPoint)velocity { |
| 810 [self stopBounce]; | 804 [self stopBounce]; |
| 811 CADisplayLink* dpLink = | 805 CADisplayLink* dpLink = |
| 812 [CADisplayLink displayLinkWithTarget:self | 806 [CADisplayLink displayLinkWithTarget:self |
| 813 selector:@selector(updateBounce)]; | 807 selector:@selector(updateBounce)]; |
| 814 [dpLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; | 808 [dpLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; |
| 815 _dpLink = dpLink; | 809 _dpLink = dpLink; |
| 816 memset(&_bounceState, 0, sizeof(_bounceState)); | 810 memset(&_bounceState, 0, sizeof(_bounceState)); |
| 817 if (self.overscrollState == ios_internal::OverscrollState::ACTION_READY) { | 811 if (self.overscrollState == OverscrollState::ACTION_READY) { |
| 818 const UIEdgeInsets insets = UIEdgeInsetsMake( | 812 const UIEdgeInsets insets = UIEdgeInsetsMake( |
| 819 -[self scrollView].contentOffset.y + self.initialContentInset, 0, 0, 0); | 813 -[self scrollView].contentOffset.y + self.initialContentInset, 0, 0, 0); |
| 820 [self setScrollViewContentInset:insets]; | 814 [self setScrollViewContentInset:insets]; |
| 821 [[self scrollView] setScrollIndicatorInsets:insets]; | 815 [[self scrollView] setScrollIndicatorInsets:insets]; |
| 822 } | 816 } |
| 823 _bounceState.yInset = [self scrollView].contentInset.top; | 817 _bounceState.yInset = [self scrollView].contentInset.top; |
| 824 _bounceState.initialYInset = _bounceState.yInset; | 818 _bounceState.initialYInset = _bounceState.yInset; |
| 825 _bounceState.headerInset = self.initialContentInset; | 819 _bounceState.headerInset = self.initialContentInset; |
| 826 _bounceState.time = CACurrentMediaTime(); | 820 _bounceState.time = CACurrentMediaTime(); |
| 827 _bounceState.velocityInset = -velocity.y * 1000.0; | 821 _bounceState.velocityInset = -velocity.y * 1000.0; |
| 828 } | 822 } |
| 829 | 823 |
| 830 - (void)stopBounce { | 824 - (void)stopBounce { |
| 831 [_dpLink invalidate]; | 825 [_dpLink invalidate]; |
| 832 _dpLink = nil; | 826 _dpLink = nil; |
| 833 if (_performingScrollViewIndependentAnimation) { | 827 if (_performingScrollViewIndependentAnimation) { |
| 834 self.overscrollState = ios_internal::OverscrollState::NO_PULL_STARTED; | 828 self.overscrollState = OverscrollState::NO_PULL_STARTED; |
| 835 _performingScrollViewIndependentAnimation = NO; | 829 _performingScrollViewIndependentAnimation = NO; |
| 836 } | 830 } |
| 837 } | 831 } |
| 838 | 832 |
| 839 - (void)updateBounce { | 833 - (void)updateBounce { |
| 840 const double time = CACurrentMediaTime(); | 834 const double time = CACurrentMediaTime(); |
| 841 const double dt = time - _bounceState.time; | 835 const double dt = time - _bounceState.time; |
| 842 CGFloat force = -_bounceState.yInset * kSpringTightness; | 836 CGFloat force = -_bounceState.yInset * kSpringTightness; |
| 843 if (_bounceState.yInset > _bounceState.headerInset) | 837 if (_bounceState.yInset > _bounceState.headerInset) |
| 844 force -= _bounceState.velocityInset * kSpringDampiness; | 838 force -= _bounceState.velocityInset * kSpringDampiness; |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 879 _panGestureRecognizer.enabled = YES; | 873 _panGestureRecognizer.enabled = YES; |
| 880 [self scrollView].panGestureRecognizer.enabled = NO; | 874 [self scrollView].panGestureRecognizer.enabled = NO; |
| 881 [self scrollView].panGestureRecognizer.enabled = YES; | 875 [self scrollView].panGestureRecognizer.enabled = YES; |
| 882 [self startBounceWithInitialVelocity:CGPointZero]; | 876 [self startBounceWithInitialVelocity:CGPointZero]; |
| 883 [self.delegate | 877 [self.delegate |
| 884 overscrollActionsController:self | 878 overscrollActionsController:self |
| 885 didTriggerAction:self.overscrollActionView.selectedAction]; | 879 didTriggerAction:self.overscrollActionView.selectedAction]; |
| 886 } | 880 } |
| 887 | 881 |
| 888 @end | 882 @end |
| OLD | NEW |