Browse Source

feat(WKWebView): Allow focus without user interaction (#540)

* [iOS] Allow focus without user interaction

* Add documentation for keyboardDisplayRequiresUserAction

* set keyboardDisplayRequiresUserAction default to true
Eric Lewis 5 years ago
parent
commit
455c30e000
5 changed files with 89 additions and 0 deletions
  1. 11
    0
      docs/Reference.md
  2. 1
    0
      ios/RNCWKWebView.h
  3. 61
    0
      ios/RNCWKWebView.m
  4. 4
    0
      ios/RNCWKWebViewManager.m
  5. 12
    0
      src/WebViewTypes.ts

+ 11
- 0
docs/Reference.md View File

42
 - [`useWebKit`](Reference.md#usewebkit)
42
 - [`useWebKit`](Reference.md#usewebkit)
43
 - [`url`](Reference.md#url)
43
 - [`url`](Reference.md#url)
44
 - [`html`](Reference.md#html)
44
 - [`html`](Reference.md#html)
45
+- [`keyboardDisplayRequiresUserAction`](Reference.md#keyboardDisplayRequiresUserAction)
45
 - [`hideKeyboardAccessoryView`](Reference.md#hidekeyboardaccessoryview)
46
 - [`hideKeyboardAccessoryView`](Reference.md#hidekeyboardaccessoryview)
46
 - [`allowsBackForwardNavigationGestures`](Reference.md#allowsbackforwardnavigationgestures)
47
 - [`allowsBackForwardNavigationGestures`](Reference.md#allowsbackforwardnavigationgestures)
47
 - [`incognito`](Reference.md#incognito)
48
 - [`incognito`](Reference.md#incognito)
756
 
757
 
757
 ---
758
 ---
758
 
759
 
760
+### `keyboardDisplayRequiresUserAction`
761
+
762
+If false, web content can programmatically display the keyboard when using the WKWebView. The default value is `true`.
763
+
764
+| Type    | Required | Platform |
765
+| ------- | -------- | -------- |
766
+| boolean | No       | iOS      |
767
+
768
+---
769
+
759
 ### `hideKeyboardAccessoryView`
770
 ### `hideKeyboardAccessoryView`
760
 
771
 
761
 If true, this will hide the keyboard accessory view (< > and Done) when using the WKWebView.
772
 If true, this will hide the keyboard accessory view (< > and Done) when using the WKWebView.

+ 1
- 0
ios/RNCWKWebView.h View File

37
 #endif
37
 #endif
38
 @property (nonatomic, assign) UIEdgeInsets contentInset;
38
 @property (nonatomic, assign) UIEdgeInsets contentInset;
39
 @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
39
 @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
40
+@property (nonatomic, assign) BOOL keyboardDisplayRequiresUserAction;
40
 @property (nonatomic, assign) BOOL hideKeyboardAccessoryView;
41
 @property (nonatomic, assign) BOOL hideKeyboardAccessoryView;
41
 @property (nonatomic, assign) BOOL allowsBackForwardNavigationGestures;
42
 @property (nonatomic, assign) BOOL allowsBackForwardNavigationGestures;
42
 @property (nonatomic, assign) BOOL incognito;
43
 @property (nonatomic, assign) BOOL incognito;

+ 61
- 0
ios/RNCWKWebView.m View File

41
 {
41
 {
42
   UIColor * _savedBackgroundColor;
42
   UIColor * _savedBackgroundColor;
43
   BOOL _savedHideKeyboardAccessoryView;
43
   BOOL _savedHideKeyboardAccessoryView;
44
+  BOOL _savedKeyboardDisplayRequiresUserAction;
44
 }
45
 }
45
 
46
 
46
 - (instancetype)initWithFrame:(CGRect)frame
47
 - (instancetype)initWithFrame:(CGRect)frame
54
     _directionalLockEnabled = YES;
55
     _directionalLockEnabled = YES;
55
     _automaticallyAdjustContentInsets = YES;
56
     _automaticallyAdjustContentInsets = YES;
56
     _contentInset = UIEdgeInsetsZero;
57
     _contentInset = UIEdgeInsetsZero;
58
+    _savedKeyboardDisplayRequiresUserAction = YES;
57
   }
59
   }
58
 
60
 
59
   // Workaround for a keyboard dismissal bug present in iOS 12
61
   // Workaround for a keyboard dismissal bug present in iOS 12
214
 
216
 
215
     [self addSubview:_webView];
217
     [self addSubview:_webView];
216
     [self setHideKeyboardAccessoryView: _savedHideKeyboardAccessoryView];
218
     [self setHideKeyboardAccessoryView: _savedHideKeyboardAccessoryView];
219
+    [self setKeyboardDisplayRequiresUserAction: _savedKeyboardDisplayRequiresUserAction];
217
     [self visitSource];
220
     [self visitSource];
218
   }
221
   }
219
 }
222
 }
364
     }
367
     }
365
 }
368
 }
366
 
369
 
370
+-(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAction
371
+{
372
+    if (_webView == nil) {
373
+        _savedKeyboardDisplayRequiresUserAction = keyboardDisplayRequiresUserAction;
374
+        return;
375
+    }
376
+  
377
+    if (_savedKeyboardDisplayRequiresUserAction == true) {
378
+        return;
379
+    }
380
+  
381
+    UIView* subview;
382
+  
383
+    for (UIView* view in _webView.scrollView.subviews) {
384
+        if([[view.class description] hasPrefix:@"WK"])
385
+            subview = view;
386
+    }
387
+  
388
+    if(subview == nil) return;
389
+  
390
+    Class class = subview.class;
391
+  
392
+    NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0};
393
+    NSOperatingSystemVersion iOS_12_2_0 = (NSOperatingSystemVersion){12, 2, 0};
394
+
395
+    Method method;
396
+    IMP override;
397
+  
398
+    if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_12_2_0]) {
399
+        // iOS 12.2.0 - Future
400
+        SEL selector = sel_getUid("_elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
401
+        method = class_getInstanceMethod(class, selector);
402
+        IMP original = method_getImplementation(method);
403
+        override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
404
+            ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
405
+        });
406
+    }
407
+    else if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) {
408
+        // iOS 11.3.0 - 12.2.0
409
+        SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:");
410
+        method = class_getInstanceMethod(class, selector);
411
+        IMP original = method_getImplementation(method);
412
+        override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, BOOL arg3, id arg4) {
413
+            ((void (*)(id, SEL, void*, BOOL, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3, arg4);
414
+        });
415
+    } else {
416
+        // iOS 9.0 - 11.3.0
417
+        SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:userObject:");
418
+        method = class_getInstanceMethod(class, selector);
419
+        IMP original = method_getImplementation(method);
420
+        override = imp_implementationWithBlock(^void(id me, void* arg0, BOOL arg1, BOOL arg2, id arg3) {
421
+            ((void (*)(id, SEL, void*, BOOL, BOOL, id))original)(me, selector, arg0, TRUE, arg2, arg3);
422
+        });
423
+    }
424
+  
425
+    method_setImplementation(method, override);
426
+}
427
+
367
 -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
428
 -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView
368
 {
429
 {
369
     if (_webView == nil) {
430
     if (_webView == nil) {

+ 4
- 0
ios/RNCWKWebViewManager.m View File

101
   view.showsVerticalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json];
101
   view.showsVerticalScrollIndicator = json == nil ? true : [RCTConvert BOOL: json];
102
 }
102
 }
103
 
103
 
104
+RCT_CUSTOM_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, BOOL, RNCWKWebView) {
105
+  view.keyboardDisplayRequiresUserAction = json == nil ? true : [RCTConvert BOOL: json];
106
+}
107
+
104
 RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script)
108
 RCT_EXPORT_METHOD(injectJavaScript:(nonnull NSNumber *)reactTag script:(NSString *)script)
105
 {
109
 {
106
   [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWKWebView *> *viewRegistry) {
110
   [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RNCWKWebView *> *viewRegistry) {

+ 12
- 0
src/WebViewTypes.ts View File

403
    */
403
    */
404
   directionalLockEnabled?: boolean;
404
   directionalLockEnabled?: boolean;
405
 
405
 
406
+  /**
407
+   * A Boolean value indicating whether web content can programmatically display the keyboard.
408
+   *
409
+   * When this property is set to true, the user must explicitly tap the elements in the
410
+   * web view to display the keyboard (or other relevant input view) for that element.
411
+   * When set to false, a focus event on an element causes the input view to be displayed
412
+   * and associated with that element automatically.
413
+   *
414
+   * The default value is `true`.
415
+   * @platform ios
416
+   */
417
+  keyboardDisplayRequiresUserAction?: boolean;
406
 }
418
 }
407
 
419
 
408
 export interface AndroidWebViewProps extends WebViewSharedProps {
420
 export interface AndroidWebViewProps extends WebViewSharedProps {