26
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

React NativeAdvent Calendar 2017

Day 1

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

Last updated at Posted at 2017-12-01

せっかく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を選択する理由の一つになると思います。

26
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?