ネイティブアプリなんだけどネイティブのコードは極力書かず、 WebViewを使ってhtml+JavaScriptを使ってアプリを組みたい。 今回はiOS, Swiftで作ってみる。
- ‘16/11/30:
UIWebView
からWKWebView
に変更
プロジェクトの作成
Xcodeでプロジェクトを新規作成する。
- Xcodeを起動して現れるダイアログで Create a new Xcode project > iOS > Application > Single View Application を選び、Nextボタンを押す
- 「Choose options for your new project」ダイアログで Product Name などの情報を入力
- 使用言語がObjective-CとSwiftから選べる
- プロジェクトを保存するパスを指定
Storyboardを使わないようにする
単にWebViewを全画面に配置するだけの単純なレイアウトなのでStoryboardは必要ない。 なのでXcodeでSwiftのプロジェクトを作った時に自動的に作られるMain.storyboardを使わないようにする。
- Xcodeでプロジェクトのターゲット > General > Deployment Info > Main Interface を空にする。
- Main.storyboardを削除する
- ViewControllerが呼び出されなくなるので、自前で呼び出す
class AppDelegate: UIResponder, UIApplicationDelegate { |
WebViewを全画面の大きさで追加
ViewControllerで全画面のWebViewを追加する。
class ViewController: UIViewController, WKNavigationDelegate { |
- WKWebViewからのイベントを処理するため、WKNavigationDelegateを継承してやる
webView
のframe
をコントローラ自体のview.bounds
にしてやると全画面になる
htmlの表示
htmlは外部httpサーバから取ってくることもできるが、ここではリソースとしてアプリ内部に持ち、 それをWebViewで表示することにする。
override func viewDidLoad() { |
- プロジェクトにResourcesとかいうグループを作り、その中にhtmlファイルを追加する (ここではindex.htmlにした)
- プロジェクトの設定のBuild Phases > Copy Bundle Resourcesで追加する (ドラッグドロップでプロジェクトにファイルを追加した場合には自動的に登録される?ので別途行う必要はないみたい)
- Bundle#url(forResource:withExtension)でリソースのパスを取得
- WKWebView#loadでWebViewに読み込む
読み込むファイルをサブディレクトリの中に入れたい場合、FinderからXcodeのResources内にサブフォルダをドラッグドロップして、Added foldersに「Create folder references」を選ぶ
htmlから画像、JavaScript、CSSを読み込む
html内で相対パスで書けばリソース内のファイルが自動的に読み込まれる
<link rel="stylesheet" type="text/css" href="main.css" /> |
JavaScriptとネイティブの連携
JavaScriptからネイティブを呼び出す
JavaScriptからネイティブに対してなにか起動するにはURLをリクエストして、 WKNavigationDelegate#webView(_:decidePolicyFor:decisionHandler:)が呼び出されるのを利用する。
スキーマをhttp://
やhttps://
じゃなくて独自のものにしておくことで判定する。
ここではnative://
などとしてみる。
html(JavaScript)側:
<p><a href="native://foo/bar.baz">Push me!</a></p> |
ネイティブ(Swift)側:
let kScheme = "native://"; |
- 呼び出されたネイティブ側では、スキーム以外のURLの残り部分を使って自由に処理すればよい
- 他にはMessage Handlerというものを使用する方法があるようです(WKUserContentController#add(_:name:))
ネイティブからJavaScriptを呼び出す
WKWebView#evaluateJavaScript(_:completionHandler:)を使用する:
func evaluateJs(_ script: String) { |
- 毎回evalすることになるし全部文字列にしないといけないのがアレだけど…
- JavaScriptの実行結果を扱いたい場合には
completionHandler
を指定してやる(省略可能)