Help us understand the problem. What is going on with this article?

React Native(iOS)で結構使えるWKWebView

More than 1 year has passed since last update.

せっかくReact NativeなのにWebView使うの?って言われそうですが、過去のWeb資産をそのまま使えるので、Markdownエディタの表示機能で採用するなど適材適所で使っていくのはありだと思います。

しかし、オフィシャルのWebviewはUIWebviewを使っているので、ブラウザの下に出てくるナビゲーションバーを消せないなど常にフルスクリーンのWebviewを使いたいときなどは使うことができません。

そこで、今回説明するのがreact-native-wkwebviewというもライブラリです。それでは見ていきましょう。

Nativeライブラリを使うのでExpoユーザはejectする必要があります。要件を整理して必要な場合に使いましょう

Installation

READMEは、npm installrnpmを使ってますが、今風に下記のようにします。

$ yarn add react-native-wkwebview-reborn
$ react-native link react-native-wkwebview-reborn

使い方

全てではありませんが、オフィシャルのWebviewと同じ名前のAPIが生えてます。ほぼほぼ同じように使えると思います。

自前のHTMLを読み込む

まずは、基本。変数にHTMLを書いて、sourceで読み込む。

const HTML = `
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
...
`;
...
 <WKWebView
    source={{ html: HTML }}

自前のHTMLの中身を動的に変更する

html内にユニークな文字列を定義しておいて、それを置換する。
そして、それをstateに入れておくと、state変更時にviewも変わる。

const HTML = `
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>THIS_TITLE</title>
  <meta charset="UTF-8">
...
`;
...
  constructor(props) {
    super(props);
    this.state = {
      html: EDITOR_HTML.replace(/THIS_TITLE/g, props.title)
    };
  }

...
 <WKWebView
    source={{ html: this.state.html }}

自前のHTMLの中身を動的に変更する(その2)

HTML内に仕掛けておいたJSの関数を走らせることでHTML/JSの状態を変更する。
refsからevaluateJavaScriptを使うことでJS関数を呼び出せる。

const HTML = `
<!DOCTYPE html>
<html lang="ja">
<head>
  <title>THIS_TITLE</title>
...
<body>
<script>
    window.receivedChangeFontSizeFromReactNative = function(fontSize) {
      document.getElementById("xterm").style["font-size"] = fontSize + "px";
    }       
</script>
`;
...
componentWillReceiveProps(nextProps) {
  if (this.props.fontSize !== nextProps.fontSize) {
    this.refs.terminal.evaluateJavaScript(
      `receivedChangeFontSizeFromReactNative(${nextProps.fontSize})`
    );
  }
}
...

render(){
  return(
     <WKWebView ref="terminal" />
  )
 }

evaluateJavaScriptでオブジェクトを渡す

evaluateJavaScriptはオブジェクトを渡せないので、JSONで文字列にシリアライズして使う。

    window.receivedCursoFromReactNative = function(cursorStr) {
      var cursor = JSON.parse(cursorStr); 
      editor.selection.moveTo(cursor.row, cursor.column)
    }
...

    let cursorStr = JSON.stringify({ row: 0, column: 0 });
    this.refs.editor.evaluateJavaScript(
      `receivedCursoFromReactNative('${cursorStr}')`
    );

自前HTMLから呼び出すJSライブラリをローカルに保存しといて読み込む

オフラインで使う場合、使いたいライブラリをファイルから読み込めます。
RNFSが別途必要なので使えるようにしておいく。

$ yearn add react-native-fs
$ react-native link react-native-fs

そして、使うライブラリをダウンロードしてきてXcodeにdrag&drop.

image.png

するとHTMLからアクセスできようになる。

注意点として、js/cssをいじった場合は、ビルドが必要になること。

const TERMINAL_HTML = `
<!DOCTYPE html>
<html lang="ja">
<head>
  <link rel="stylesheet" href="xterm.css" />
  <script src="xterm.js"></script> 
...
`;
...
      <WKWebView
            source={{ html: this.state.html, baseUrl: RNFS.MainBundlePath }} />

JS内でのイベントをReact Nativeコンポーネントに渡す。

HTMLのJSイベントをRN側に渡せます。例えば、チャットのソケット通信をWebviewで実装しておいて、結果をRN側に渡すなどですね。RNに渡せば、Reduxなどで状態管理できますね。

const HTML = `
...
    editor.getSession().on("change", function(e){
      var content = editor.getValue();
      window.webkit.messageHandlers.reactNative.postMessage({content: content});
    });
...

_onMessage(message) {
  if (message.body.content) {
     ...
  }
}
...
   <WKWebView
    onMessage={this._onMessage.bind(this)} />
...    
`;

インターネット上のURLを開き、JSをその都度走らせる。

ロードが終わるたびにjsが走る。自前HTMLのように変数にJSを書いて入れる。
注意点として、loadするたびに入るので明示的にreload()してやる必要がある。

下記の例はwindowサイズが変わったときの挙動を変えています。

let zoomOutJs = `
var metaTag=document.createElement('meta');
metaTag.name = "viewport";
metaTag.id="viewport-meta";
metaTag.content = "user-scalable=no,width=BROWSER_WIDTH";
document.getElementsByTagName('head')[0].appendChild(metaTag);
`;

   <WKWebView
     source={{ uri: "https://www.google.com" }}
     injectedJavaScript={zoomOutJS.replace("BROWSER_WIDTH", width)}
   />

ドキュメントされてないが生えてるメソッド

refで参照可能。詳しくはソース参照

  • this.refs.terminal.reload()
  • this.refs.terminal.goBack()
  • this.refs.terminal.goForward()
 <WKWebView refs="terminal">

target="blank"などのリンクを踏んだときにSafariで開かないで自身のwebviewで開く

<WKWebview
    openNewWindowInWebView={true} />

(< > and Done)のナビゲーションを表示しない

<WKWebview
    hideKeyboardAccessoryView={true} />

 まとめ

DHHもつぶやいてますが、iPhoneのjetStream(JSベンチマーク)スコアは、andoroidを凌駕しています。
iphone8に限ればMBPに僅差とか。こういった事実もiPhoneでWebViewを選択する理由の一つになると思います。


Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした