diff --git a/README.md b/README.md index 2c015b10..f393382c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ React Native comes with [WebView](http://facebook.github.io/react-native/docs/webview.html) component, which uses UIWebView on iOS. This component uses [WKWebView](http://nshipster.com/wkwebkit/) introduced in iOS 8 with all the performance boost. -**Deployment Target >= iOS 8.0 is required** *(which is React Native's current minimum deployment target anyway).* +**Deployment Target >= iOS 8.0 is required** _(which is React Native's current minimum deployment target anyway)._ ### Install @@ -17,10 +17,9 @@ React Native comes with [WebView](http://facebook.github.io/react-native/docs/we 2. In the XCode's "Project navigator", right click on your project's Libraries folder ➜ Add Files to <...> 3. Go to node_modules ➜ react-native-wkwebview-reborn ➜ ios ➜ select `RCTWKWebView.xcodeproj` 4. Go your build target ➜ Build Phases ➜ Link Binary With Libraries, click "+" and select `libRCTWkWebView.a` (see the following screenshot for reference) -![Linking](https://user-images.githubusercontent.com/608221/28060167-0650e3f4-6659-11e7-8085-7a8c2615f90f.png) + ![Linking](https://user-images.githubusercontent.com/608221/28060167-0650e3f4-6659-11e7-8085-7a8c2615f90f.png) 5. Compile and profit (Remember to set Minimum Deployment Target = 8.0) - ### Usage ```js @@ -29,7 +28,7 @@ import WKWebView from 'react-native-wkwebview-reborn'; Try replacing your existing `WebView` with `WKWebView` and it should work in most cases. -For React Native >= 0.57, use version 2.x; for React Native < 0.40, use version 0.x. +For React Native >= 0.57, use version 2.x; for React Native < 0.57, use version 1.x; for React Native < 0.40, use version 0.x. ### Compatibility with UIWebView @@ -44,7 +43,7 @@ WKWebView aims to be a drop-in replacement for UIWebView. However, some legacy U A callback to get the loading progress of WKWebView. Derived from [`estimatedProgress`](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKWebView_Ref/#//apple_ref/occ/instp/WKWebView/estimatedProgress) property. ```js - console.log(progress)} /> + console.log(progress)} /> ``` `progress` is a double between 0 and 1. @@ -59,7 +58,7 @@ If set to true, links with `target="_blank"` or `window.open` will be opened in - **sendCookies** -Set `sendCookies` to true to copy cookies from `sharedHTTPCookieStorage` when calling loadRequest. This emulates the behavior of react-native's `WebView` component. You can set cookies using `react-native-cookies` Default is false. +Set `sendCookies` to true to copy cookies from `sharedHTTPCookieStorage` when calling loadRequest. This emulates the behavior of react-native's `WebView` component. You can set cookies using `react-native-cookies` Default is false. - **source={{file: '', allowingReadAccessToURL: '' }}** @@ -67,7 +66,12 @@ This allows WKWebView loads a local HTML file. Please note the underlying API is It allows you to provide a fallback URL for iOS 8 users. ```js - + ``` You can also use the `require` syntax (sendCookies and userAgent will be ignored) @@ -94,7 +98,7 @@ This property specifies how the safe area insets are used to modify the content - **keyboardDisplayRequiresUserAction** -Enables focusing an input inside a webview and showing the keyboard *programatically*. **New in 1.20.0** +Enables focusing an input inside a webview and showing the keyboard _programatically_. **New in 1.20.0** - **keyboardDismissMode** @@ -135,11 +139,11 @@ Enable horizontal swipe gestures will trigger back-forward navigations. Derived - pagingEnabled - scrollEnabled - directionalLockEnabled +- scalesPageToFit -#### Unsupported props are: +#### Unsupported props are: - mediaPlaybackRequiresUserAction -- scalesPageToFit - domStorageEnabled - javaScriptEnabled - allowsInlineMediaPlayback @@ -147,7 +151,6 @@ Enable horizontal swipe gestures will trigger back-forward navigations. Derived ### Advanced Communication between React Native and WkWebView - #### Communication from WKWebview to React Native - **onMessage** @@ -155,19 +158,19 @@ Enable horizontal swipe gestures will trigger back-forward navigations. Derived This utilizes the message handlers in WKWebView and allows you to post message from webview to React Native. For example: ```js - console.log(e)} /> + console.log(e)} /> ``` Then in your webview, you can post message to React Native using ```js -window.webkit.messageHandlers.reactNative.postMessage({message: 'hello!'}); +window.webkit.messageHandlers.reactNative.postMessage({ message: 'hello!' }); ``` or (since 1.14.0) ```js -window.postMessage({message: 'hello!'}); +window.postMessage({ message: 'hello!' }); ``` Then you can access the nativeEvent in React Native using the event object returned @@ -198,7 +201,7 @@ you can define a callback method on your WebView: window.receivedMessageFromReactNative = function(data) { // Code here console.log(data); -} +}; ``` Then you can send message from React Native with this method call: diff --git a/WKWebView.ios.js b/WKWebView.ios.js index a24024a2..a499d3e3 100644 --- a/WKWebView.ios.js +++ b/WKWebView.ios.js @@ -11,7 +11,7 @@ import ReactNative, { ViewPropTypes, NativeModules, Text, - ActivityIndicator + ActivityIndicator, } from 'react-native'; import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'; @@ -40,10 +40,10 @@ const NavigationType = keyMirror({ const JSNavigationScheme = 'react-js-navigation'; type ErrorEvent = { - domain: any; - code: any; - description: any; -} + domain: any, + code: any, + description: any, +}; type Event = Object; @@ -54,18 +54,10 @@ const defaultRenderLoading = () => ( ); const defaultRenderError = (errorDomain, errorCode, errorDesc) => ( - - Error loading page - - - {'Domain: ' + errorDomain} - - - {'Error Code: ' + errorCode} - - - {'Description: ' + errorDesc} - + Error loading page + {'Domain: ' + errorDomain} + {'Error Code: ' + errorCode} + {'Description: ' + errorDesc} ); @@ -80,15 +72,9 @@ class WKWebView extends React.Component { static propTypes = { ...ViewPropTypes, - html: deprecatedPropType( - PropTypes.string, - 'Use the `source` prop instead.' - ), + html: deprecatedPropType(PropTypes.string, 'Use the `source` prop instead.'), - url: deprecatedPropType( - PropTypes.string, - 'Use the `source` prop instead.' - ), + url: deprecatedPropType(PropTypes.string, 'Use the `source` prop instead.'), /** * Loads static html or a uri (with optional headers) in the WebView. @@ -252,16 +238,16 @@ class WKWebView extends React.Component { allowsLinkPreview: PropTypes.bool, /** * Sets the customized user agent by using of the WKWebView - */ + */ customUserAgent: PropTypes.string, userAgent: PropTypes.string, /** * A Boolean value that determines whether paging is enabled for the scroll view. - */ + */ pagingEnabled: PropTypes.bool, /** * A Boolean value that sets whether diagonal scrolling is allowed. - */ + */ directionalLockEnabled: PropTypes.bool, /* * The manner in which the keyboard is dismissed when a drag begins in the @@ -272,6 +258,10 @@ class WKWebView extends React.Component { 'on-drag', 'interactive', // iOS only ]), + /** + * Scale webview to match the device viewport. + */ + scalesPageToFit: PropTypes.bool, }; state = { @@ -293,39 +283,39 @@ class WKWebView extends React.Component { otherView = (this.props.renderLoading || defaultRenderLoading)(); } else if (this.state.viewState === WebViewState.ERROR) { const errorEvent = this.state.lastErrorEvent; - invariant( - errorEvent != null, - 'lastErrorEvent expected to be non-null' - ); + invariant(errorEvent != null, 'lastErrorEvent expected to be non-null'); otherView = (this.props.renderError || defaultRenderError)( errorEvent.domain, errorEvent.code, errorEvent.description ); } else if (this.state.viewState !== WebViewState.IDLE) { - console.error( - 'CRAWKWebView invalid state encountered: ' + this.state.loading - ); + console.error('CRAWKWebView invalid state encountered: ' + this.state.loading); } const webViewStyles = [styles.container, styles.webView, this.props.style]; - if (this.state.viewState === WebViewState.LOADING || - this.state.viewState === WebViewState.ERROR) { + if ( + this.state.viewState === WebViewState.LOADING || + this.state.viewState === WebViewState.ERROR + ) { // if we're in either LOADING or ERROR states, don't show the webView webViewStyles.push(styles.hidden); } - const onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => { - const shouldStart = this.props.onShouldStartLoadWithRequest && - this.props.onShouldStartLoadWithRequest(event.nativeEvent); - WKWebViewManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier); - }); + const onShouldStartLoadWithRequest = + this.props.onShouldStartLoadWithRequest && + ((event: Event) => { + const shouldStart = + this.props.onShouldStartLoadWithRequest && + this.props.onShouldStartLoadWithRequest(event.nativeEvent); + WKWebViewManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier); + }); let source = this.props.source; if (this.props.source && typeof this.props.source === 'object') { source = Object.assign({}, this.props.source, { sendCookies: this.props.sendCookies, - customUserAgent: this.props.customUserAgent || this.props.userAgent + customUserAgent: this.props.customUserAgent || this.props.userAgent, }); if (this.props.html) { @@ -337,9 +327,11 @@ class WKWebView extends React.Component { const messagingEnabled = typeof this.props.onMessage === 'function'; - const webView = + const webView = ( { this.webview = ref; }} + ref={ref => { + this.webview = ref; + }} key="webViewKey" style={webViewStyles} contentInsetAdjustmentBehavior={this.props.contentInsetAdjustmentBehavior} @@ -369,7 +361,9 @@ class WKWebView extends React.Component { directionalLockEnabled={this.props.directionalLockEnabled} onNavigationResponse={this._onNavigationResponse} keyboardDismissMode={this.props.keyboardDismissMode} - />; + scalesPageToFit={this.props.scalesPageToFit} + /> + ); return ( @@ -435,7 +429,7 @@ class WKWebView extends React.Component { this.getWebViewHandle(), UIManager.CRAWKWebView.Commands.stopLoading, null - ) + ); }; /** @@ -448,7 +442,7 @@ class WKWebView extends React.Component { * document.addEventListener('message', e => { document.title = e.data; }); * ``` */ - postMessage = (data) => { + postMessage = data => { UIManager.dispatchViewManagerCommand( this.getWebViewHandle(), UIManager.CRAWKWebView.Commands.postMessage, @@ -456,7 +450,7 @@ class WKWebView extends React.Component { ); }; - evaluateJavaScript = (js) => { + evaluateJavaScript = js => { return WKWebViewManager.evaluateJavaScript(this.getWebViewHandle(), js); }; @@ -492,7 +486,7 @@ class WKWebView extends React.Component { this.setState({ lastErrorEvent: event.nativeEvent, - viewState: WebViewState.ERROR + viewState: WebViewState.ERROR, }); }; @@ -523,8 +517,8 @@ class WKWebView extends React.Component { _onNavigationResponse = (event: Event) => { const { onNavigationResponse } = this.props; - onNavigationResponse && onNavigationResponse(event) - } + onNavigationResponse && onNavigationResponse(event); + }; } const CRAWKWebView = requireNativeComponent('CRAWKWebView', WKWebView, { @@ -534,7 +528,7 @@ const CRAWKWebView = requireNativeComponent('CRAWKWebView', WKWebView, { onLoadingFinish: true, onMessage: true, messagingEnabled: PropTypes.bool, - } + }, }); const styles = StyleSheet.create({ @@ -570,7 +564,7 @@ const styles = StyleSheet.create({ }, webView: { backgroundColor: '#ffffff', - } + }, }); export default WKWebView; diff --git a/ios/RCTWKWebView/CRAWKWebView.h b/ios/RCTWKWebView/CRAWKWebView.h index fe6e2346..e96759c2 100644 --- a/ios/RCTWKWebView/CRAWKWebView.h +++ b/ios/RCTWKWebView/CRAWKWebView.h @@ -30,6 +30,7 @@ shouldStartLoadForRequest:(NSMutableDictionary *)request @property (nonatomic, assign) UIEdgeInsets contentInset; @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; @property (nonatomic, assign) BOOL messagingEnabled; +@property (nonatomic, assign) BOOL scalesPageToFit; @property (nonatomic, assign) BOOL allowsLinkPreview; @property (nonatomic, assign) BOOL openNewWindowInWebView; @property (nonatomic, assign) BOOL injectJavaScriptForMainFrameOnly; diff --git a/ios/RCTWKWebView/CRAWKWebView.m b/ios/RCTWKWebView/CRAWKWebView.m index fdc27ce3..bd20106e 100644 --- a/ios/RCTWKWebView/CRAWKWebView.m +++ b/ios/RCTWKWebView/CRAWKWebView.m @@ -84,6 +84,7 @@ - (instancetype)initWithProcessPool:(WKProcessPool *)processPool _webView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever; } #endif + [self setViewportToDeviceWidthScript]; [self setupPostMessageScript]; [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil]; [self addSubview:_webView]; @@ -126,6 +127,11 @@ - (void)setMessagingEnabled:(BOOL)messagingEnabled { [self setupPostMessageScript]; } +- (void)setScalesPageToFit:(BOOL)scalesPageToFit { + _scalesPageToFit = scalesPageToFit; + [self setViewportToDeviceWidthScript]; +} + - (void)resetupScripts { [_webView.configuration.userContentController removeAllUserScripts]; [self setupPostMessageScript]; @@ -137,6 +143,19 @@ - (void)resetupScripts { } } +- (void)setViewportToDeviceWidthScript { + if (_scalesPageToFit) { + NSString *jScript = @"var meta = document.createElement('meta');" + "meta.setAttribute('name', 'viewport');" + "meta.setAttribute('content', 'width=device-width');" + "document.getElementsByTagName('head')[0].appendChild(meta);"; + WKUserScript *script =[[WKUserScript alloc] initWithSource:jScript + injectionTime:WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:_injectedJavaScriptForMainFrameOnly]; + [_webView.configuration.userContentController addUserScript:script]; + } +} + - (void)setupPostMessageScript { if (_messagingEnabled) { NSString *source = @"window.originalPostMessage = window.postMessage;" diff --git a/ios/RCTWKWebView/CRAWKWebViewManager.m b/ios/RCTWKWebView/CRAWKWebViewManager.m index fcd88f3b..e6579ee9 100644 --- a/ios/RCTWKWebView/CRAWKWebViewManager.m +++ b/ios/RCTWKWebView/CRAWKWebViewManager.m @@ -72,6 +72,7 @@ - (UIView *)view RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL) RCT_EXPORT_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, BOOL) RCT_EXPORT_VIEW_PROPERTY(messagingEnabled, BOOL) +RCT_EXPORT_VIEW_PROPERTY(scalesPageToFit, BOOL) RCT_EXPORT_VIEW_PROPERTY(allowsLinkPreview, BOOL) #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */ RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInsetAdjustmentBehavior)