Skip to content

Commit

Permalink
feat: Add visionOS support (react-native-webview#3373)
Browse files Browse the repository at this point in the history
* Add visionOS support

* One more ifdef

* Adopt UIEditMenuConfiguration

* Update RNCWebViewImpl.m

* Update RNCWebViewImpl.m

* Update metro config

* Update ios-ci.yml

---------

Co-authored-by: Thibault Malbranche <malbranche.thibault@gmail.com>
  • Loading branch information
Saadnajmi and Titozzz authored May 7, 2024
1 parent 7119160 commit dd7fb83
Show file tree
Hide file tree
Showing 9 changed files with 1,796 additions and 74 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ios-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
working-directory: example/ios
- name: Build iOS test app
run: |
device_name='iPhone 13'
device_name='iPhone 15'
device=$(xcrun simctl list devices "${device_name}" available | grep "${device_name} (")
re='\(([-0-9A-Fa-f]+)\)'
[[ $device =~ $re ]] || exit 1
Expand Down
8 changes: 8 additions & 0 deletions apple/RNCWebViewImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request

@end

#if !TARGET_OS_OSX
@interface RNCWebViewImpl : RCTView <UIEditMenuInteractionDelegate>

@property (nonatomic, nullable) UIEditMenuInteraction *editMenuInteraction API_AVAILABLE(ios(16.0));
#else
@interface RNCWebViewImpl : RCTView
#endif // !TARGET_OS_OSX


@property (nonatomic, copy) RCTDirectEventBlock onFileDownload;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
Expand Down
81 changes: 56 additions & 25 deletions apple/RNCWebViewImpl.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

NSString *const CUSTOM_SELECTOR = @"_CUSTOM_SELECTOR_";

#if !TARGET_OS_OSX
#if TARGET_OS_IOS
// runtime trick to remove WKWebView keyboard default toolbar
// see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279
@interface _SwizzleHelperWK : UIView
Expand All @@ -46,7 +46,7 @@ -(id)inputAccessoryView
return nil;
}
@end
#endif // !TARGET_OS_OSX
#endif // TARGET_OS_IOS

@interface RNCWKWebView : WKWebView
#if !TARGET_OS_OSX
Expand Down Expand Up @@ -170,10 +170,10 @@ - (instancetype)initWithFrame:(CGRect)frame
_autoManageStatusBarEnabled = YES;
_contentInset = UIEdgeInsetsZero;
_savedKeyboardDisplayRequiresUserAction = YES;
#if !TARGET_OS_OSX
#if TARGET_OS_IOS
_savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
_savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
#endif // !TARGET_OS_OSX
#endif // TARGET_OS_IOS
_injectedJavaScript = nil;
_injectedJavaScriptForMainFrameOnly = YES;
_injectedJavaScriptBeforeContentLoaded = nil;
Expand All @@ -188,10 +188,16 @@ - (instancetype)initWithFrame:(CGRect)frame
_enableApplePay = NO;
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 /* iOS 15 */
_mediaCapturePermissionGrantType = RNCWebViewPermissionGrantType_Prompt;
#endif
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 160000 /* iOS 15 */
if (@available(iOS 16.0, *)) {
_editMenuInteraction = [[UIEditMenuInteraction alloc] initWithDelegate:self];
[self addInteraction:_editMenuInteraction];
}
#endif
}

#if !TARGET_OS_OSX
#if TARGET_OS_IOS
[[NSNotificationCenter defaultCenter]addObserver:self
selector:@selector(appDidBecomeActive)
name:UIApplicationDidBecomeActiveNotification
Expand Down Expand Up @@ -226,7 +232,7 @@ - (instancetype)initWithFrame:(CGRect)frame
object:nil];

}
#endif // !TARGET_OS_OSX
#endif // TARGET_OS_IOS
return self;
}

Expand All @@ -246,29 +252,51 @@ - (void)startLongPress:(UILongPressGestureRecognizer *)pressSender
if (pressSender.state != UIGestureRecognizerStateEnded || !self.menuItems) {
return;
}
// When a long press ends, bring up our custom UIMenu if defined
if (self.menuItems.count == 0) {
if (@available(iOS 16.0, *)) {
CGPoint location = [pressSender locationInView:self];
UIEditMenuConfiguration *config = [UIEditMenuConfiguration configurationWithIdentifier:nil sourcePoint:location];
[_editMenuInteraction presentEditMenuWithConfiguration:config];
} else {
// When a long press ends, bring up our custom UIMenu if defined
if (self.menuItems.count == 0) {
UIMenuController *menuController = [UIMenuController sharedMenuController];
menuController.menuItems = nil;
[menuController setMenuVisible:NO animated:YES];
[menuController showMenuFromView:self rect:self.bounds];
return;
}
UIMenuController *menuController = [UIMenuController sharedMenuController];
NSMutableArray *menuControllerItems = [NSMutableArray arrayWithCapacity:self.menuItems.count];

for(NSDictionary *menuItem in self.menuItems) {
NSString *menuItemLabel = [RCTConvert NSString:menuItem[@"label"]];
NSString *menuItemKey = [RCTConvert NSString:menuItem[@"key"]];
NSString *sel = [NSString stringWithFormat:@"%@%@", CUSTOM_SELECTOR, menuItemKey];
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle: menuItemLabel
action: NSSelectorFromString(sel)];
[menuControllerItems addObject: item];
}
}

menuController.menuItems = menuControllerItems;
[menuController setMenuVisible:YES animated:YES];
UIMenuController *menuController = [UIMenuController sharedMenuController];
NSMutableArray *menuControllerItems = [NSMutableArray arrayWithCapacity:self.menuItems.count];

for(NSDictionary *menuItem in self.menuItems) {
NSString *menuItemLabel = [RCTConvert NSString:menuItem[@"label"]];
NSString *menuItemKey = [RCTConvert NSString:menuItem[@"key"]];
NSString *sel = [NSString stringWithFormat:@"%@%@", CUSTOM_SELECTOR, menuItemKey];
UIMenuItem *item = [[UIMenuItem alloc] initWithTitle: menuItemLabel
action: NSSelectorFromString(sel)];
[menuControllerItems addObject: item];
}
menuController.menuItems = menuControllerItems;
[menuController showMenuFromView:self rect:self.bounds];
}
}

- (UIMenu *)editMenuInteraction:(UIEditMenuInteraction *)interaction menuForConfiguration:(UIEditMenuConfiguration *)configuration suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
{
NSMutableArray<UICommand *> *menuItems = [NSMutableArray new];
for(NSDictionary *menuItem in self.menuItems) {
NSString *menuItemLabel = [RCTConvert NSString:menuItem[@"label"]];
NSString *menuItemKey = [RCTConvert NSString:menuItem[@"key"]];
NSString *sel = [NSString stringWithFormat:@"%@%@", CUSTOM_SELECTOR, menuItemKey];
UICommand *command = [UICommand commandWithTitle:menuItemLabel
image:nil
action:NSSelectorFromString(sel)
propertyList:nil];
[menuItems addObject:command];
}
UIMenu *menu = [UIMenu menuWithChildren:menuItems];
return menu;
}
#endif // !TARGET_OS_OSX

- (void)dealloc
Expand Down Expand Up @@ -529,6 +557,7 @@ - (void)didMoveToWindow
[self setKeyboardDisplayRequiresUserAction: _savedKeyboardDisplayRequiresUserAction];
[self visitSource];
}

#if !TARGET_OS_OSX
// Allow this object to recognize gestures
if (self.menuItems != nil) {
Expand Down Expand Up @@ -594,7 +623,7 @@ - (void)removeFromSuperview
#endif
}

#if !TARGET_OS_OSX
#if TARGET_OS_IOS
-(void)showFullScreenVideoStatusBars
{
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
Expand Down Expand Up @@ -650,7 +679,7 @@ -(void)keyboardDisplacementFix
}];
}
}
#endif // !TARGET_OS_OSX
#endif // TARGET_OS_IOS

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
Expand Down Expand Up @@ -840,6 +869,7 @@ -(void)setSuppressMenuItems:(NSArray<NSString *> *)suppressMenuItems {
_webView.suppressMenuItems = suppressMenuItems;
}

#if TARGET_OS_IOS
-(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAction
{
if (_webView == nil) {
Expand Down Expand Up @@ -946,6 +976,7 @@ -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView

object_setClass(subview, newClass);
}
#endif // TARGET_OS_IOS

- (void)__addInputAccessoryView {
UIView* subview;
Expand Down
5 changes: 5 additions & 0 deletions example/visionos/Podfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require_relative '../../node_modules/react-native-test-app/visionos/test_app'

workspace 'WebviewExample.xcworkspace'

use_test_app!
Loading

0 comments on commit dd7fb83

Please sign in to comment.