Browse Source

feat(onScroll): Add `onScroll` callback for iOS & Android (#516)

* Add `onScroll` callback for iOS & Android

This code was mostly extracted from https://github.com/react-native-community/react-native-webview/pull/202

I tried and tried to make it work with `Animated.event`'s `useNativeDriver`, but I was unsuccessful 😢 that'll have to be done later once I understand better how Animated's native stuff is hooked up.

* fix crash for missing onScroll
Jared Forsyth 5 years ago
parent
commit
e4c8dab2ae

+ 25
- 0
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java View File

31
 import android.webkit.WebViewClient;
31
 import android.webkit.WebViewClient;
32
 import android.widget.FrameLayout;
32
 import android.widget.FrameLayout;
33
 
33
 
34
+import com.facebook.react.views.scroll.ScrollEvent;
35
+import com.facebook.react.views.scroll.ScrollEventType;
36
+import com.facebook.react.views.scroll.OnScrollDispatchHelper;
34
 import com.facebook.react.bridge.Arguments;
37
 import com.facebook.react.bridge.Arguments;
35
 import com.facebook.react.bridge.LifecycleEventListener;
38
 import com.facebook.react.bridge.LifecycleEventListener;
36
 import com.facebook.react.bridge.ReactContext;
39
 import com.facebook.react.bridge.ReactContext;
446
     }
449
     }
447
     export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
450
     export.put(TopLoadingProgressEvent.EVENT_NAME, MapBuilder.of("registrationName", "onLoadingProgress"));
448
     export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
451
     export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
452
+    export.put(ScrollEventType.SCROLL.getJSEventName(), MapBuilder.of("registrationName", "onScroll"));
449
     return export;
453
     return export;
450
   }
454
   }
451
 
455
 
780
     protected @Nullable
784
     protected @Nullable
781
     RNCWebViewClient mRNCWebViewClient;
785
     RNCWebViewClient mRNCWebViewClient;
782
     protected boolean sendContentSizeChangeEvents = false;
786
     protected boolean sendContentSizeChangeEvents = false;
787
+    private final OnScrollDispatchHelper mOnScrollDispatchHelper = new OnScrollDispatchHelper();
783
 
788
 
784
     /**
789
     /**
785
      * WebView must be created with an context of the current activity
790
      * WebView must be created with an context of the current activity
886
       dispatchEvent(this, new TopMessageEvent(this.getId(), message));
891
       dispatchEvent(this, new TopMessageEvent(this.getId(), message));
887
     }
892
     }
888
 
893
 
894
+    protected void onScrollChanged(int x, int y, int oldX, int oldY) {
895
+      super.onScrollChanged(x, y, oldX, oldY);
896
+
897
+      if (mOnScrollDispatchHelper.onScrollChanged(x, y)) {
898
+        ScrollEvent event = ScrollEvent.obtain(
899
+                this.getId(),
900
+                ScrollEventType.SCROLL,
901
+                x,
902
+                y,
903
+                mOnScrollDispatchHelper.getXFlingVelocity(),
904
+                mOnScrollDispatchHelper.getYFlingVelocity(),
905
+                this.computeHorizontalScrollRange(),
906
+                this.computeVerticalScrollRange(),
907
+                this.getWidth(),
908
+                this.getHeight());
909
+
910
+        dispatchEvent(this, event);
911
+      }
912
+    }
913
+
889
     protected void cleanupCallbacksAndDestroy() {
914
     protected void cleanupCallbacksAndDestroy() {
890
       setWebViewClient(null);
915
       setWebViewClient(null);
891
       destroy();
916
       destroy();

+ 25
- 0
ios/RNCWKWebView.m View File

34
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingProgress;
34
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingProgress;
35
 @property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
35
 @property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
36
 @property (nonatomic, copy) RCTDirectEventBlock onMessage;
36
 @property (nonatomic, copy) RCTDirectEventBlock onMessage;
37
+@property (nonatomic, copy) RCTDirectEventBlock onScroll;
37
 @property (nonatomic, copy) WKWebView *webView;
38
 @property (nonatomic, copy) WKWebView *webView;
38
 @end
39
 @end
39
 
40
 
509
   if (!_scrollEnabled) {
510
   if (!_scrollEnabled) {
510
     scrollView.bounds = _webView.bounds;
511
     scrollView.bounds = _webView.bounds;
511
   }
512
   }
513
+  else if (_onScroll != nil) {
514
+    NSDictionary *event = @{
515
+      @"contentOffset": @{
516
+          @"x": @(scrollView.contentOffset.x),
517
+          @"y": @(scrollView.contentOffset.y)
518
+          },
519
+      @"contentInset": @{
520
+          @"top": @(scrollView.contentInset.top),
521
+          @"left": @(scrollView.contentInset.left),
522
+          @"bottom": @(scrollView.contentInset.bottom),
523
+          @"right": @(scrollView.contentInset.right)
524
+          },
525
+      @"contentSize": @{
526
+          @"width": @(scrollView.contentSize.width),
527
+          @"height": @(scrollView.contentSize.height)
528
+          },
529
+      @"layoutMeasurement": @{
530
+          @"width": @(scrollView.frame.size.width),
531
+          @"height": @(scrollView.frame.size.height)
532
+          },
533
+      @"zoomScale": @(scrollView.zoomScale ?: 1),
534
+      };
535
+    _onScroll(event);
536
+  }
512
 }
537
 }
513
 
538
 
514
 - (void)setDirectionalLockEnabled:(BOOL)directionalLockEnabled
539
 - (void)setDirectionalLockEnabled:(BOOL)directionalLockEnabled

+ 1
- 0
ios/RNCWKWebViewManager.m View File

56
  */
56
  */
57
 RCT_EXPORT_VIEW_PROPERTY(messagingEnabled, BOOL)
57
 RCT_EXPORT_VIEW_PROPERTY(messagingEnabled, BOOL)
58
 RCT_EXPORT_VIEW_PROPERTY(onMessage, RCTDirectEventBlock)
58
 RCT_EXPORT_VIEW_PROPERTY(onMessage, RCTDirectEventBlock)
59
+RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock)
59
 
60
 
60
 RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)message)
61
 RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)message)
61
 {
62
 {

+ 1
- 0
src/WebView.ios.tsx View File

382
         onLoadingProgress={this.onLoadingProgress}
382
         onLoadingProgress={this.onLoadingProgress}
383
         onLoadingStart={this.onLoadingStart}
383
         onLoadingStart={this.onLoadingStart}
384
         onMessage={this.onMessage}
384
         onMessage={this.onMessage}
385
+        onScroll={this.props.onScroll}
385
         onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
386
         onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
386
         ref={this.webViewRef}
387
         ref={this.webViewRef}
387
         scalesPageToFit={scalesPageToFit}
388
         scalesPageToFit={scalesPageToFit}

+ 7
- 0
src/WebViewTypes.ts View File

7
   NativeMethodsMixin,
7
   NativeMethodsMixin,
8
   Constructor,
8
   Constructor,
9
   UIManagerStatic,
9
   UIManagerStatic,
10
+  NativeScrollEvent,
10
 } from 'react-native';
11
 } from 'react-native';
11
 
12
 
12
 export interface WebViewCommands {
13
 export interface WebViewCommands {
213
   injectedJavaScript?: string;
214
   injectedJavaScript?: string;
214
   mediaPlaybackRequiresUserAction?: boolean;
215
   mediaPlaybackRequiresUserAction?: boolean;
215
   messagingEnabled: boolean;
216
   messagingEnabled: boolean;
217
+  onScroll?: (event: NativeScrollEvent) => void;
216
   onLoadingError: (event: WebViewErrorEvent) => void;
218
   onLoadingError: (event: WebViewErrorEvent) => void;
217
   onLoadingFinish: (event: WebViewNavigationEvent) => void;
219
   onLoadingFinish: (event: WebViewNavigationEvent) => void;
218
   onLoadingProgress: (event: WebViewProgressEvent) => void;
220
   onLoadingProgress: (event: WebViewProgressEvent) => void;
542
    */
544
    */
543
   renderLoading?: () => ReactElement;
545
   renderLoading?: () => ReactElement;
544
 
546
 
547
+  /**
548
+   * Function that is invoked when the `WebView` scrolls.
549
+   */
550
+  onScroll?: (event: NativeScrollEvent) => void;
551
+
545
   /**
552
   /**
546
    * Function that is invoked when the `WebView` has finished loading.
553
    * Function that is invoked when the `WebView` has finished loading.
547
    */
554
    */