Developer's Blog

【iOS】UIWebView Hacks 〜ブラウザ開発テクニック〜



こんにちは。
Sleipnir Mobile for iPhone / iPad 開発担当の宮本です。

調べてみると、Sleipnir Mobile の最初のコミットは 2010年9月6日で、もう3年近く開発しているようです。最初はこんな貧弱な API でブラウザなんて作れるわけ無いと思っていましたが、なんとかなるものですね。今では多くのユーザーさんに使っていただけるプロダクトになりました。

今回は、この3年間で、使ってきた UIWebView の技を紹介したいと思います。詳細な方法については書きませんが、UIWebView で何ができて何ができないのかはわかるかと思います。それぞれの詳細なやり方については機会があれば書いていきたいです。

目次

長くなりそうなので、目次を用意しました。
余裕があればアップデートするので、ブックマークでもしておいてください。

UIWebView の API でできること

  • 戻る/進む
  • リロード/中止
  • HTMLをロードする
  • JavaScript を実行する (これがあるので、なんでもできる!)
  • ロードの開始前/開始/失敗/完了をフック

頑張ったらできること

実現が難しいこと

頑張ったらできること

JavaScript のコードを埋め込む

基本です。貧弱な API は JavaScript でカバーするしかありません。
js ファイルをリソースで用意しておいて、ロード完了時に埋め込みます。

JavaScript から Objective-C にメッセージを送る

これができれば、色々応用できますね。

例えば、JavaScript側で
location.href=”sleipnir://send?text=hello” を実行します。
webView:shouldStartLoadWithRequest:navigationType:
request.URL.scheme‘sleipnir’ なら、URL の scheme 以外の値に応じて何か処理して、NO を返す。

サムネイルを作る

UIWebViewUIView なので layerrenderInContext: できます。
問題は、タイミングです。ロード完了時にサムネイルを作りたい場合、webView:didFinishLoad: がきたからといって、レンダリングが終わったわけではありません。少し待ってから renderInContext: すると上手くいきます。

タイトルを取得する

[webView stringByEvaluatingJavaScriptFromString:@”document.title”];
カテゴリでも書いておきましょう。

ユーザーエージェントを変更する

iOS 4 と iOS 5, 6 で方法が違います。今後のバージョンでも変わるかもしれません。

  • iOS 4
    NSURLRequestvalueForHTTPHeaderField: を swizzle して違う値を返す。
  • iOS 5, 6
    NSUserDefaultsregisterDefaults“UserAgent” をキーに、値を入れておけば勝手に使ってくれる。

新規タブリンクをハンドルする

  1. JavaScript で最後に触った Element を保持できるようにしておく
  2. webContentView:shouldStartLoadWithRequest:navigationType:
    のタイミングでその Element の target の値をチェックします。

スクロールイベントをハンドルする

スクロールでナビゲーションバーを一緒に動かすようなものを書く場合、必要です。
UIScrollViewDelegate のメソッドを swizzle してます。

タッチイベントをハンドルする

UIWindow のサブクラスを書いて、sendEvent: で全イベントをフックする。Sleipnir Mobile ではここで Notification をあげてます。
Sleipnir Mobile の L 閉じなどのジェスチャはこれでできます。タブ切り替えはもう少しややこしいです。

リンク長押しをハンドルする

  1. document.body.style.webkitTouchCallout=’none’ を実行して、長押しアクションシートを止める
  2. 上で取得できるタッチイベントの位置にあるリンク を JavaScirpt で取得する。
  3. アクションシートを出すなり、Hold And Go のように背面でリンクを開くなり好きにする。

少し古い記事ですが Sleipnir Mobile では下記の方法でリンク検出精度をあげてます。
https://blog.fenrir-inc.com/jp/2011/06/sleipnir_mobile_13_hold_and_go.html

Basic 認証に対応する

NSURLProtocol のサブクラスを書いて、registerClass する。
startLoadingstopLoading をオーバーライドして、自分で NSURLConnection 作るので、connection:didReceiveAuthenticationChallenge: をフックできる。

パスワードの保存

  1. 標準の delegate で POST はフックできるので、そこで JavaScript で入力値を取得
  2. ロード完了時に JavaScript で保存した値を入力

リソースフィルター

NSURLCachesetSharedURLCache でカスタムな NSURLCache を設定すれば、すべてのリソースロードをフックできるので、特定のドメインなどの条件で空データを返せばフィルタリングできます。

プログレスを取得する

WebKit にあるプログレスは取得できませんが、NJKWebViewProgress で十分そうですね。素晴らしい!
https://github.com/ninjinkun/NJKWebViewProgress

開いている画像、PDF を保存する

UIWebView で表示しているということはダウンロードできているということなのですが、データを取り出す方法がありません。保存する場合は、再度ダウンロードします。

ページ内検索

こっちに書きました。(2013/11/15)
【iOS】JavaScript を使って、UIWebView でページ内検索

頑張ってもできないこと

今のところ簡単なやり方がわかってないものです。何かと技はあるので、まだまだできるものがあるかもしれません。

Web ページを PDF にする

Macなら簡単にできるのに、できません。

証明書を取得する

かなり頑張ればできそうな気もするのですが、今のところできてません。Mac でも大変です。

戻る進む履歴を取得する

UIWebView からは取得できません。自前で戻る進む履歴を管理すればなんとかなるかもしれません。

UIWebView の property を監視

なぜかできません。Mac の WebView は監視できます。

文字コードの自動判別

Web ページで文字コードを正しく設定してないと化けることがあります。
UIWebViewloadData:MIMEType:textEncodingName:baseURL:
でエンコードを指定して NSData をロードすることはできます。

UIWebView で頑張るブラウザSleipnir Mobile for iPhone / iPad はこちら


Copyright © 2019 Fenrir Inc. All rights reserved.