diff --git a/WKWebView.ios.js b/WKWebView.ios.js index a24024a2..a4cac12b 100644 --- a/WKWebView.ios.js +++ b/WKWebView.ios.js @@ -173,6 +173,8 @@ class WKWebView extends React.Component { * Report the progress */ onProgress: PropTypes.func, + onCanGoBackChange: PropTypes.func, + onCanGoForwardChange: PropTypes.func, /** * A function that is invoked when the webview calls `window.postMessage`. * Setting this property will inject a `postMessage` global into your @@ -362,6 +364,8 @@ class WKWebView extends React.Component { onLoadingError={this._onLoadingError} messagingEnabled={messagingEnabled} onProgress={this._onProgress} + onCanGoBackChange={this._onCanGoBackChange} + onCanGoForwardChange={this._onCanGoForwardChange} onMessage={this._onMessage} onScroll={this._onScroll} onShouldStartLoadWithRequest={onShouldStartLoadWithRequest} @@ -511,6 +515,16 @@ class WKWebView extends React.Component { onProgress && onProgress(event.nativeEvent.progress); }; + _onCanGoBackChange = (event: Event) => { + const onCanGoBackChange = this.props.onCanGoBackChange; + onCanGoBackChange && onCanGoBackChange(event.nativeEvent.canGoBack); + } + + _onCanGoForwardChange = (event: Event) => { + const onCanGoForwardChange = this.props.onCanGoForwardChange; + onCanGoForwardChange && onCanGoForwardChange(event.nativeEvent.canGoForward); + } + _onMessage = (event: Event) => { var { onMessage } = this.props; onMessage && onMessage(event); diff --git a/ios/RCTWKWebView/CRAWKWebView.m b/ios/RCTWKWebView/CRAWKWebView.m index fdc27ce3..7353c7a9 100644 --- a/ios/RCTWKWebView/CRAWKWebView.m +++ b/ios/RCTWKWebView/CRAWKWebView.m @@ -34,6 +34,8 @@ @interface CRAWKWebView () = 110000 /* __IPHONE_11_0 */ // `contentInsetAdjustmentBehavior` is only available since iOS 11. // We set the default behavior to "never" so that iOS @@ -86,6 +88,8 @@ - (instancetype)initWithProcessPool:(WKProcessPool *)processPool #endif [self setupPostMessageScript]; [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; + [_webView addObserver:self forKeyPath:@"canGoBack" options:NSKeyValueObservingOptionNew context:nil]; + [_webView addObserver:self forKeyPath:@"canGoForward" options:NSKeyValueObservingOptionNew context:nil]; [self addSubview:_webView]; } return self; @@ -163,7 +167,7 @@ - (void)loadRequest:(NSURLRequest *)request request = mutableRequest; } } - + [_webView loadRequest:request]; } @@ -179,29 +183,29 @@ -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView if (!hideKeyboardAccessoryView) { return; } - + UIView* subview; for (UIView* view in _webView.scrollView.subviews) { if([[view.class description] hasPrefix:@"WKContent"]) subview = view; } - + if(subview == nil) return; - + NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelperWK", subview.class.superclass]; Class newClass = NSClassFromString(name); - + if(newClass == nil) { newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0); if(!newClass) return; - + Method method = class_getInstanceMethod([_SwizzleHelperWK class], @selector(inputAccessoryView)); class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method)); - + objc_registerClassPair(newClass); } - + object_setClass(subview, newClass); } @@ -212,7 +216,7 @@ -(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAct if (!keyboardDisplayRequiresUserAction) { Class class = NSClassFromString(@"WKContentView"); NSOperatingSystemVersion iOS_11_3_0 = (NSOperatingSystemVersion){11, 3, 0}; - + if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion: iOS_11_3_0]) { SEL selector = sel_getUid("_startAssistingNode:userIsInteracting:blurPreviousNode:changingActivityState:userObject:"); Method method = class_getInstanceMethod(class, selector); @@ -314,20 +318,20 @@ - (void)setSource:(NSDictionary *)source if ([source[@"customUserAgent"] length] != 0 && [_webView respondsToSelector:@selector(setCustomUserAgent:)]) { [_webView setCustomUserAgent:source[@"customUserAgent"]]; } - + // Allow loading local files: // // Only works for iOS 9+. So iOS 8 will simply ignore those two values NSString *file = [RCTConvert NSString:source[@"file"]]; NSString *allowingReadAccessToURL = [RCTConvert NSString:source[@"allowingReadAccessToURL"]]; - + if (file && [_webView respondsToSelector:@selector(loadFileURL:allowingReadAccessToURL:)]) { NSURL *fileURL = [RCTConvert NSURL:file]; NSURL *baseURL = [RCTConvert NSURL:allowingReadAccessToURL]; [_webView loadFileURL:fileURL allowingReadAccessToURL:baseURL]; return; } - + // Check for a static html source first NSString *html = [RCTConvert NSString:source[@"html"]]; if (html) { @@ -338,7 +342,7 @@ - (void)setSource:(NSDictionary *)source [_webView loadHTMLString:html baseURL:baseURL]; return; } - + NSURLRequest *request = [RCTConvert NSURLRequest:source]; // Because of the way React works, as pages redirect, we actually end up // passing the redirect urls back here, so we ignore them if trying to load @@ -391,7 +395,7 @@ - (UIColor *)backgroundColor @"canGoBack": @(_webView.canGoBack), @"canGoForward" : @(_webView.canGoForward), }]; - + return event; } @@ -413,6 +417,20 @@ - (void)observeValueForKeyPath:(NSString *)keyPath } _onProgress(@{@"progress": [change objectForKey:NSKeyValueChangeNewKey]}); } + + if ([keyPath isEqualToString:@"canGoBack"]) { + if (!_onCanGoBackChange) { + return; + } + _onCanGoBackChange(@{@"canGoBack": [change objectForKey:NSKeyValueChangeNewKey]}); + } + + if ([keyPath isEqualToString:@"canGoForward"]) { + if (!_onCanGoForwardChange) { + return; + } + _onCanGoForwardChange(@{@"canGoForward": [change objectForKey:NSKeyValueChangeNewKey]}); + } } - (void)dealloc @@ -468,9 +486,9 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W NSURLRequest *request = navigationAction.request; NSURL* url = request.URL; NSString* scheme = url.scheme; - + BOOL isJSNavigation = [scheme isEqualToString:RCTJSNavigationScheme]; - + // handle mailto and tel schemes if ([scheme isEqualToString:@"mailto"] || [scheme isEqualToString:@"tel"]) { if ([app canOpenURL:url]) { @@ -479,7 +497,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W return; } } - + // skip this for the JS Navigation handler if (!isJSNavigation && _onShouldStartLoadWithRequest) { NSMutableDictionary *event = [self baseEvent]; @@ -493,7 +511,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W return decisionHandler(WKNavigationActionPolicyCancel); } } - + if (_onLoadingStart) { // We have this check to filter out iframe requests and whatnot BOOL isTopFrame = [url isEqual:request.mainDocumentURL]; @@ -506,7 +524,7 @@ - (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(W _onLoadingStart(event); } } - + if (isJSNavigation) { decisionHandler(WKNavigationActionPolicyCancel); } @@ -525,7 +543,7 @@ - (void)webView:(__unused WKWebView *)webView didFailProvisionalNavigation:(__un // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os return; } - + NSMutableDictionary *event = [self baseEvent]; [event addEntriesFromDictionary:@{ @"domain": error.domain, @@ -548,7 +566,7 @@ - (void)webView:(WKWebView *)webView didFinishNavigation:(__unused WKNavigation - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; - + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Close", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { completionHandler(); }]]; @@ -557,7 +575,7 @@ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSStrin } - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler { - + // TODO We have to think message to confirm "YES" UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { @@ -571,17 +589,17 @@ - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSStr } - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler { - + UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:nil preferredStyle:UIAlertControllerStyleAlert]; [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) { textField.text = defaultText; }]; - + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { NSString *input = ((UITextField *)alertController.textFields.firstObject).text; completionHandler(input); }]]; - + [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) { completionHandler(nil); }]]; diff --git a/ios/RCTWKWebView/CRAWKWebViewManager.m b/ios/RCTWKWebView/CRAWKWebViewManager.m index fcd88f3b..39d6704b 100644 --- a/ios/RCTWKWebView/CRAWKWebViewManager.m +++ b/ios/RCTWKWebView/CRAWKWebViewManager.m @@ -67,6 +67,8 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onProgress, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onCanGoBackChange, RCTDirectEventBlock) +RCT_EXPORT_VIEW_PROPERTY(onCanGoForwardChange, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onMessage, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(onScroll, RCTDirectEventBlock) RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL)