とあるWebサービスのiPhoneアプリクライアントを作っていた時の話です
JavaScriptとObjective-Cを連携するにはdocument.location
をいじればいいのですが、この処理を今あるWebサイト全体に入れるのはかなり手間ですしリンクの処理を上書きしたい時はブラウザの挙動が変わってしまうのでアプリ用のWebサイトを別途用意する必要がありました(´・ω・`)
そこでブラウザから普通に見れるサイトがアプリを通して見るといろんな仕掛けが顔をだすような仕組みを作りたかったのでUIWebViewを継承して作ってみました。
ClassHookWebView
詳しい使い方はREADME.mdの方に書いたのでここでは簡単な使い方と仕組みについて解説したいと思います
特徴
- UIWebViewと同じように使える(Delegateも使える)
- Webサイト側はタップの時にアプリのメソッドを呼び出したいDOMにクラスをつけるだけ
- <a>タグの挙動はブラウザから見ても変わらない
使い方
Webサイト側
タップの時にアプリのメソッドを呼び出したいDOMにクラスをつける
1 2 |
<a class="app-action" href="somewhere">Tap</a> |
終わり\(^o^)/
アプリ側
ClassHookWebViewをUIWebViewと同じように初期化して配置する
1 2 3 4 |
ClassHookWebView *chWebView = [[ClassHookWebView alloc] initWithFrame:self.view.bounds]; [chWebView setDelegate:self]; [self.view addSubview:chWebView] |
ロードする前にタップを取得したいDOMについているクラスとタップ時に実行するメソッドを登録する
1 2 |
[chWebView addTarget:self action:@selector(someaction) forClass:@"app-action"]; |
終わり\(^o^)/
Example
詳しい動作はClassHookWebViewからクローンして付属しているXCodeプロジェクトを実行すると確認できます
仕組み
どうやって動いているのかというととても単純でロードが終わったら特定のクラスが付いているDOMをとってきてJavaScriptで動作を書き換えてやってます。UIWebViewにはstringByEvaluatingJavaScriptFromString:
という便利なメソッドがあって現在表示されているページ上で任意のJavaScriptを実行することができます。これを使って
- href属性の値を取得
- data-href属性に入れなおす
- href属性には
javascript:void(0)
を入れる - addEventListenerでclickイベントの時に
document.location
を書き換えるように設定
という処理をDOM毎に行っています。
href属性から値をとっているのは<a>タグの処理を上書きしたかったらで<div>タグであっても無理やりクラスとhref属性を指定してやれば値のやり取りをすることもできますw
Delegateはセッターで別オブジェクトに設定するようにしてUIWebViewDelegate Protocolで指定されているメソッドを全部上書きして設定したオブジェクトに流すようにしています!
大体の流れはこんな感じで更に詳しくは是非コードを読んでみてください(^O^)/
ClassHookWebView
ひろせ