Source/WebKit/ChangeLog

 12018-06-19 Jer Noble <jer.noble@apple.com>
 2
 3 [Fullscreen] Add a pinch-to-exit gesture
 4 https://bugs.webkit.org/show_bug.cgi?id=186821
 5
 6 Reviewed by Tim Horton.
 7
 8 Add a pinch gesture recognizer that overrides the pan gesture recognizer when active. Hide the
 9 WKFullscreenViewController's controls while a dismiss gesture is active.
 10
 11 * UIProcess/ios/fullscreen/WKFullScreenViewController.h:
 12 * UIProcess/ios/fullscreen/WKFullScreenViewController.mm:
 13 (-[WKFullScreenViewController setAnimating:]):
 14 (-[WKFullScreenViewController prefersStatusBarHidden]):
 15 (-[WKFullScreenViewController gestureRecognizer:shouldReceiveTouch:]):
 16 (-[WKFullScreenViewController _touchDetected:]):
 17 * UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm:
 18 (-[WKFullscreenAnimationController context]):
 19 (-[WKFullscreenAnimationController updateWithProgress:scale:translation:anchor:]):
 20 (-[WKFullScreenInteractiveTransition animator]):
 21 (-[WKFullScreenInteractiveTransition updateInteractiveTransition:withScale:andTranslation:]):
 22 (-[WKFullScreenWindowController enterFullScreen]):
 23 (-[WKFullScreenWindowController beganExitFullScreenWithInitialFrame:finalFrame:]):
 24 (-[WKFullScreenWindowController interactionControllerForDismissal:]):
 25 (-[WKFullScreenWindowController _startToDismissFullscreenChanged:]):
 26 (-[WKFullScreenWindowController _dismissFullscreenViewController]):
 27 (-[WKFullScreenWindowController _interactiveDismissChanged:]):
 28 (-[WKFullScreenWindowController _interactivePinchDismissChanged:]):
 29
1302018-06-22 Jer Noble <jer.noble@apple.com>
231
332 [Fullscreen] Home indicator should show and hide with status bar

Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenViewController.h

@@NS_ASSUME_NONNULL_BEGIN
4141@property (assign, nonatomic) BOOL prefersHomeIndicatorAutoHidden;
4242@property (assign, nonatomic, getter=isPlaying) BOOL playing;
4343@property (assign, nonatomic, getter=isPictureInPictureActive) BOOL pictureInPictureActive;
 44@property (assign, nonatomic, getter=isAnimating) BOOL animating;
4445
4546- (id)initWithWebView:(WKWebView *)webView;
4647- (void)showUI;

Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenViewController.mm

@@- (void)setPictureInPictureActive:(BOOL)active
250250 [_pipButton setSelected:active];
251251}
252252
 253- (void)setAnimating:(BOOL)animating
 254{
 255 if (_animating == animating)
 256 return;
 257 _animating = animating;
 258 [self setNeedsStatusBarAppearanceUpdate];
 259
 260 if (_animating)
 261 [self hideUI];
 262 else
 263 [self showUI];
 264}
 265
253266#pragma mark - UIViewController Overrides
254267
255268- (void)loadView

@@- (UIStatusBarStyle)preferredStatusBarStyle
348361
349362- (BOOL)prefersStatusBarHidden
350363{
351  return _prefersStatusBarHidden;
 364 return _animating || _prefersStatusBarHidden;
352365}
353366
354367#pragma mark - UIGestureRecognizerDelegate

@@- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
360373
361374- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
362375{
363  [self showUI];
 376 if (!self.animating)
 377 [self showUI];
364378 return YES;
365379}
366380

@@- (void)_touchDetected:(id)sender
419433 if (score > requiredScore)
420434 [self _showPhishingAlert];
421435 }
422  [self showUI];
 436 if (!self.animating)
 437 [self showUI];
423438}
424439
425440- (void)_statusBarFrameDidChange:(NSNotificationCenter *)notification

Source/WebKit/UIProcess/ios/fullscreen/WKFullScreenWindowControllerIOS.mm

@@@property (retain, nonatomic) UIViewController* viewController;
145145@property (nonatomic) CGRect initialFrame;
146146@property (nonatomic) CGRect finalFrame;
147147@property (nonatomic, getter=isAnimatingIn) BOOL animatingIn;
 148@property (readonly, nonatomic) id<UIViewControllerContextTransitioning> context;
148149@end
149150
150151@implementation WKFullscreenAnimationController {

@@- (void)dealloc
167168 [super dealloc];
168169}
169170
 171- (id<UIViewControllerContextTransitioning>)context
 172{
 173 return _context.get();
 174}
 175
170176- (void)_createViewsForTransitionContext:(id<UIViewControllerContextTransitioning>)transitionContext
171177{
172178 _maskView = adoptNS([[UIView alloc] init]);

@@- (void)updateWithProgress:(CGFloat)progress
274280 window.backgroundColor = [UIColor colorWithWhite:0 alpha:_initialBackgroundAlpha + progress * (_finalBackgroundAlpha - _initialBackgroundAlpha)];
275281}
276282
 283- (void)updateWithProgress:(CGFloat)progress scale:(CGFloat)scale translation:(CGSize)translation anchor:(CGPoint)anchor
 284{
 285 CGAffineTransform progressTransform = _initialAnimatingViewTransform;
 286 progressTransform = CGAffineTransformScale(progressTransform, scale, scale);
 287 progressTransform = CGAffineTransformTranslate(progressTransform, translation.width, translation.height);
 288 [_animatingView setTransform:progressTransform];
 289
 290 UIWindow *window = [_animatingView window];
 291 window.backgroundColor = [UIColor colorWithWhite:0 alpha:_initialBackgroundAlpha + progress * (_finalBackgroundAlpha - _initialBackgroundAlpha)];
 292}
 293
277294- (void)updateWithProgress:(CGFloat)progress translation:(CGSize)translation anchor:(CGPoint)anchor
278295{
279296 CGAffineTransform progressTransform = _initialAnimatingViewTransform;

@@@end
313330
314331@interface WKFullScreenInteractiveTransition : NSObject<UIViewControllerInteractiveTransitioning>
315332- (id)initWithAnimator:(WKFullscreenAnimationController *)animator anchor:(CGPoint)point;
 333@property (nonatomic, readonly) WKFullscreenAnimationController *animator;
316334@end
317335
318336@implementation WKFullScreenInteractiveTransition {

@@- (id)initWithAnimator:(WKFullscreenAnimationController *)animator anchor:(CGPoi
331349 return self;
332350}
333351
 352- (WKFullscreenAnimationController *)animator
 353{
 354 return _animator.get();
 355}
 356
334357- (BOOL)wantsInteractiveStart
335358{
336359 return YES;

@@- (void)updateInteractiveTransition:(CGFloat)progress withTranslation:(CGSize)tr
348371 [_context updateInteractiveTransition:progress];
349372}
350373
 374- (void)updateInteractiveTransition:(CGFloat)progress withScale:(CGFloat)scale andTranslation:(CGSize)translation
 375{
 376 [_animator updateWithProgress:progress scale:scale translation:translation anchor:_anchor];
 377 [_context updateInteractiveTransition:progress];
 378}
 379
351380- (void)cancelInteractiveTransition
352381{
353382 [_animator end:YES];

@@@implementation WKFullScreenWindowController {
395424 RetainPtr<UIViewController> _viewControllerForPresentation;
396425 RetainPtr<WKFullScreenViewController> _fullscreenViewController;
397426 RetainPtr<UISwipeGestureRecognizer> _startDismissGestureRecognizer;
398  RetainPtr<UIPanGestureRecognizer> _interactiveDismissGestureRecognizer;
 427 RetainPtr<UIPanGestureRecognizer> _interactivePanDismissGestureRecognizer;
 428 RetainPtr<UIPinchGestureRecognizer> _interactivePinchDismissGestureRecognizer;
399429 RetainPtr<WKFullScreenInteractiveTransition> _interactiveDismissTransitionCoordinator;
400430
401431 CGRect _initialFrame;

@@- (void)enterFullScreen
480510 _fullscreenViewController.get().view.frame = _rootViewController.get().view.bounds;
481511 [self _updateLocationInfo];
482512
483  _startDismissGestureRecognizer = adoptNS([[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(_startToDismissFullscreenChanged:)]);
 513 _startDismissGestureRecognizer = adoptNS([[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(_startToDismissFullscreenChanged:)]);
484514 [_startDismissGestureRecognizer setDelegate:self];
485515 [_startDismissGestureRecognizer setCancelsTouchesInView:YES];
486516 [_startDismissGestureRecognizer setNumberOfTouchesRequired:1];
487517 [_startDismissGestureRecognizer setDirection:UISwipeGestureRecognizerDirectionDown];
488518 [_fullscreenViewController.get().view addGestureRecognizer:_startDismissGestureRecognizer.get()];
489519
490  _interactiveDismissGestureRecognizer = adoptNS([[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(_interactiveDismissChanged:)]);
491  [_interactiveDismissGestureRecognizer setDelegate:self];
492  [_interactiveDismissGestureRecognizer setCancelsTouchesInView:NO];
493  [_fullscreenViewController.get().view addGestureRecognizer:_interactiveDismissGestureRecognizer.get()];
 520 _interactivePanDismissGestureRecognizer = adoptNS([[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(_interactiveDismissChanged:)]);
 521 [_interactivePanDismissGestureRecognizer setDelegate:self];
 522 [_interactivePanDismissGestureRecognizer setCancelsTouchesInView:NO];
 523 [_fullscreenViewController.get().view addGestureRecognizer:_interactivePanDismissGestureRecognizer.get()];
 524
 525 _interactivePinchDismissGestureRecognizer = adoptNS([[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(_interactivePinchDismissChanged:)]);
 526 [_interactivePinchDismissGestureRecognizer setDelegate:self];
 527 [_interactivePinchDismissGestureRecognizer setCancelsTouchesInView:NO];
 528 [_fullscreenViewController.get().view addGestureRecognizer:_interactivePinchDismissGestureRecognizer.get()];
494529
495530 [self _manager]->saveScrollPosition();
496531

@@- (void)beganExitFullScreenWithInitialFrame:(CGRect)initialFrame finalFrame:(CGR
640675 return;
641676 }
642677
643  [_fullscreenViewController dismissViewControllerAnimated:YES completion:^{
644  if (![self._webView _page])
645  return;
646 
647  [self _completedExitFullScreen];
648  }];
 678 [self _dismissFullscreenViewController];
649679}
650680
651681- (void)_completedExitFullScreen

@@- (id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissa
774804 if (![animator isKindOfClass:[WKFullscreenAnimationController class]])
775805 return nil;
776806
777  if (!_interactiveDismissTransitionCoordinator)
778  _interactiveDismissTransitionCoordinator = adoptNS([[WKFullScreenInteractiveTransition alloc] initWithAnimator:(WKFullscreenAnimationController *)animator anchor:CGPointZero]);
 807 if (!_interactiveDismissTransitionCoordinator) {
 808 CGPoint anchor = CGPointZero;
 809 if (_interactivePanDismissGestureRecognizer.get().state == UIGestureRecognizerStateBegan)
 810 anchor = [_interactivePanDismissGestureRecognizer locationInView:_fullscreenViewController.get().view];
 811 else if (_interactivePinchDismissGestureRecognizer.get().state == UIGestureRecognizerStateBegan)
 812 anchor = [_interactivePinchDismissGestureRecognizer locationInView:_fullscreenViewController.get().view];
 813 _interactiveDismissTransitionCoordinator = adoptNS([[WKFullScreenInteractiveTransition alloc] initWithAnimator:(WKFullscreenAnimationController *)animator anchor:anchor]);
 814 }
779815
780816 return _interactiveDismissTransitionCoordinator.get();
781817}

@@- (WebFullScreenManagerProxy*)_manager
901937
902938- (void)_startToDismissFullscreenChanged:(id)sender
903939{
 940 if (_inInteractiveDismiss)
 941 return;
904942 _inInteractiveDismiss = true;
 943 [self _dismissFullscreenViewController];
 944}
 945
 946- (void)_dismissFullscreenViewController
 947{
 948 [_fullscreenViewController setAnimating:YES];
905949 [_fullscreenViewController dismissViewControllerAnimated:YES completion:^{
906950 if (![self._webView _page])
907951 return;
908952
 953
 954 if (_interactiveDismissTransitionCoordinator.get().animator.context.transitionWasCancelled) {
 955 [_fullscreenViewController setAnimating:NO];
 956 return;
 957 }
 958
 959 _interactiveDismissTransitionCoordinator = nil;
 960
909961 [self _completedExitFullScreen];
910  [_fullscreenViewController setPrefersStatusBarHidden:YES];
911962 }];
912963}
913964

@@- (void)_interactiveDismissChanged:(id)sender
916967 if (!_inInteractiveDismiss)
917968 return;
918969
919  CGPoint translation = [_interactiveDismissGestureRecognizer translationInView:_fullscreenViewController.get().view];
920  CGPoint velocity = [_interactiveDismissGestureRecognizer velocityInView:_fullscreenViewController.get().view];
 970 auto pinchState = [_interactivePinchDismissGestureRecognizer state];
 971 if (pinchState > UIGestureRecognizerStatePossible && pinchState <= UIGestureRecognizerStateEnded)
 972 return;
 973
 974 CGPoint translation = [_interactivePanDismissGestureRecognizer translationInView:_fullscreenViewController.get().view];
 975 CGPoint velocity = [_interactivePanDismissGestureRecognizer velocityInView:_fullscreenViewController.get().view];
921976 CGFloat progress = translation.y / (_fullscreenViewController.get().view.bounds.size.height / 2);
922977 progress = std::min(1., std::max(0., progress));
923978
924  if (_interactiveDismissGestureRecognizer.get().state == UIGestureRecognizerStateEnded) {
 979 if (_interactivePanDismissGestureRecognizer.get().state == UIGestureRecognizerStateEnded) {
925980 _inInteractiveDismiss = false;
926981
927982 if (progress > 0.25 || (progress > 0 && velocity.y > 5))
928983 [self requestExitFullScreen];
929  else {
 984 else
930985 [_interactiveDismissTransitionCoordinator cancelInteractiveTransition];
931  _interactiveDismissTransitionCoordinator = nil;
932  }
933986 return;
934987 }
935988
936989 [_interactiveDismissTransitionCoordinator updateInteractiveTransition:progress withTranslation:CGSizeMake(translation.x, translation.y)];
937990}
938991
 992- (void)_interactivePinchDismissChanged:(id)sender
 993{
 994 if (!_inInteractiveDismiss && _interactivePinchDismissGestureRecognizer.get().state == UIGestureRecognizerStateBegan) {
 995 [self _startToDismissFullscreenChanged:sender];
 996 return;
 997 }
 998
 999 CGFloat scale = [_interactivePinchDismissGestureRecognizer scale];
 1000 CGFloat velocity = [_interactivePinchDismissGestureRecognizer velocity];
 1001 CGFloat progress = std::min(1., std::max(0., 1 - scale));
 1002
 1003 CGPoint translation = CGPointZero;
 1004 auto panState = [_interactivePanDismissGestureRecognizer state];
 1005 if (panState > UIGestureRecognizerStatePossible && panState <= UIGestureRecognizerStateEnded)
 1006 translation = [_interactivePanDismissGestureRecognizer translationInView:_fullscreenViewController.get().view];
 1007
 1008 if (_interactivePinchDismissGestureRecognizer.get().state == UIGestureRecognizerStateEnded) {
 1009 _inInteractiveDismiss = false;
 1010 if ((progress > 0.05 && velocity < 0.) || velocity < -2.5)
 1011 [self requestExitFullScreen];
 1012 else
 1013 [_interactiveDismissTransitionCoordinator cancelInteractiveTransition];
 1014 return;
 1015 }
 1016
 1017 [_interactiveDismissTransitionCoordinator updateInteractiveTransition:progress withScale:scale andTranslation:CGSizeMake(translation.x, translation.y)];
 1018}
 1019
9391020@end
9401021
9411022#endif // PLATFORM(IOS) && ENABLE(FULLSCREEN_API)