iOS WebView App

10,504 views
10,155 views

Published on

社内iOS WebVeiwアプリ勉強会の資料に補足追加した物。
iOSのハイブリッドアプリ開発における、UIのパフォーマンスや設計について。

Published in: Technology
0 Comments
28 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
10,504
On SlideShare
0
From Embeds
0
Number of Embeds
1,603
Actions
Shares
0
Downloads
44
Comments
0
Likes
28
Embeds 0
No embeds

No notes for slide

iOS WebView App

  1. 1. iOS WebView App @hagino3000 2013-12-12 社内的な勉強会 atVOYAGE GROUP
  2. 2. 注 Androidの話は無いです
  3. 3. なぜWebView上にアプリを 構築するのか • 任意のタイミングでアプリケーション をアップデートしたい • WebでUI作った方が楽そうだから • そんな事は無い → あとで死ぬ
  4. 4. 実装戦略
  5. 5. 実装戦略 • HTML, CSS, JSのリソースをアプリにバン ドルする or NOT • Single Page Web App or NOT • ネイティブUIパーツを使う or NOT
  6. 6. HTML, CSS, JSファイルを アプリにバンドルするか • 完全にオフラインで動作させる事が可能 • それ以外メリット無し
  7. 7. Single Page Web Appにするか • Single Page • JSの実装量は増える • なんらかのJSフレームワークを使う Sencha Touch, AngularJS etc... • Multi Page • 作りはシンプルになる
  8. 8. (番外編) Multi UIWebView • ページ毎にUIWebViewを作る • ネイティブのトランジションが使える • 試してない
  9. 9. iOSのUIパーツを使うか
  10. 10. iOSのUIパーツを使うか • NavigationBar, UIAlertView, UIActionSheet etc.. • 使った方が楽 (モーダル制御等) • ネイティブUIを呼び出すブリッジを作る • ブラウザでデバッグできるようにPolyfillを 用意しておく
  11. 11. UIWebView
  12. 12. UIWebView iOSVersion Browser Component iOS 6 iOS Safari 6.x iOS 7 iOS Safari 7.0 機能比較 http://caniuse.com/#compare=ios_saf+6.0-6.1,ios_saf+7.0
  13. 13. コンテンツの配置
  14. 14. UIWebView UIScrollView HTML Content (表示領域) Navigation BarContentsArea(iOS6) ContentsArea(iOS7) document.scrollTop 不可視 不可視
  15. 15. ViewPort <!-- よくある設定 --> <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0" />
  16. 16. スクロールの制御 • Single Page Web App Height 100%のブロック要素を並べて、 その中でスクロールさせる場合はデフ ォルトのスクロールが邪魔になる。 (二重スクロール) • そうでない場合はscrollViewのスクロー ルを使う
  17. 17. スクロールの制御 // UIScrollViewのスクロールを無効にする webView.scrollView.scrollEnabled = NO; /* CSSでブロック要素の慣性スクロールを有効にする */ article.page{ box-sizing: border-box; position: absolute; width: 100%; height: 100%; top 0; left: 0; overflow: scroll; -webkit-overflow-scrolling: touch; }
  18. 18. スクロール速度 scrollView.decelerationRate = UIScrollViewDecelerationRateNormal; // デフォルトは UIScrollViewDecelerationRateFast 慣性スクロールの減速が緩くなる。scrollViewのスクロ ールを使う場合は設定する。 CSSでブロック要素の慣性スクロールを有効にした場 合は UIScrollViewRateNormal 相当になる。
  19. 19. NavigationBar • ポジション固定にする場合はネイティブのを 使うと楽 • NavigationBarを半透明にして、背後にもコンテ ンツを配置したい (iOS7) • 初期表示でnavbarの下にコンテンツが潜りこ まないようにscrollViewの調節が必要 • webView.scrollView.contentInset • webView.scrollView.contentOffset • webView.scrollView.scrollIndicatorInsets
  20. 20. position: fixed • 効かない • スクロールに引きずられる • iScroll • onScrollイベントを監視してposition:fixedを再 現している • https://github.com/cubiq/iscroll • 代替策として別のViewにして配置するのもアリ
  21. 21. テキスト入力 1. <input type=”text”> or <textarea> にフォーカス 2. Keyboard Windowがせり上ってくる 3. document.scrollTopがずれる • テキスト入力パーツはなるべく画面上部に配置す る。下の方だと最悪Keyboard Windowが被る。 • scrollViewのスクロールを無効にしているとユーザ ーが元に戻せない。 →入力後にscrollTopを調整する。 • JavaScriptからキーボードの上げ下げを可能にして おく
  22. 22. その他よくやる設定 <!-- 電話番号らしき数列をリンクにするのを無効化 --> <meta name="format-detection" content="telephone=no"> body { /* 文字列のコピーや選択をできなくする */ -webkit-user-select:none; /* リンクの長押しメニューを出なくする */ -webkit-touch-callout:none; /* アンカー要素をタップした時に出現する枠を見えなくする */ -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
  23. 23. ネイティブとの連係
  24. 24. To WebView NSString *js = @”window.App.hello();”; [self.webView stringByEvaluationgJavaScriptFromString:js]; // 例:ネイティブからsessionId, userId etc.. を渡す NSString *js = [NSString stringWithFormat: @”window.App.deviceReady({ sessionId: ‘%@’, userId: ‘%@’, apiBase: ‘%@’ });”, self.sessionId, self.user_id, @”http://api.hoge.com”]; [self.webView stringByEvaluatingJavaScriptFromString:js];
  25. 25. From WebView 1. 独自スキーマを指定してlocationを変更 2. UIWebViewDelegateのshouldStartLoadWithRequest でフック 3. なにかやる 4. UIWebViewのstringByEvaluatingJavaScriptFromString で結果を返す ヒント:PhoneGapのソースの方がこのスライドよりも参考になる https://github.com/phonegap/phonegap/blob/master/lib/ios/CordovaLib/cordova.js
  26. 26. // JS側の実装例 var transactionId = 0; function nativeBridge(action, params, callback) { // 後で呼び出すためにコールバック関数を保持する var transactionId = transactionId++; callbacks[transactionId] = callback; var data = encodeURIComponent(JSON.stringify({ transactionId: transactionId, params: params })); // iframeのlocation変更リクエストはWebViewDelegateで // フックできる var frame = $('<iframe>').attr('src', 'MyActionHandler://' + action + '/' + data ).css({display: "none"}); $(document.body).append(frame); frame.remove(); }
  27. 27. // Objective-C - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { // Check schema if ([[request.URL.scheme lowercaseString] isEqualToString:@”MyActionHandler”]) { // なんかやる return NO; } return YES; }
  28. 28. App.nativeBridge(‘alert’, { title: “Error”, message:“Sorry...”, buttonTitle: “OK” }, function(){ // UIAlertViewのOKボタンがタップされた }) 使用例
  29. 29. 最初から作っておくと便利 • キーボードWindowの上げ下げ • Alert • Confirm • ActionSheet • ジェスチャー認識
  30. 30. リクエストのフック • UIWebViewDelegateのshouldStartLoadWithRequest ⃝ ロケーション変更 (iframe含む) ⃝ リロード (iframe含む) ⃝ フォームサブミット × XMLHttpRequest × script要素、link要素、img要素による通信 • NSURLProtocol • 全ての通信をフックできる、ホワイトリストを 実装するならこちら
  31. 31. リクエストのフックの応用 • ロケーション変更時、ドメインによって処理を変 える(ex 自サービスの外に出る場合はSafariで開く) • 認証情報の付加 • NSMutableURLRequestを操作してcookieや独自 ヘッダを付加する • ホワイトリスト (NSURLProtocol) • しかし、アドネットワークを利用して広告を表 示しているとホワイトリストのメンテは困難を 極める
  32. 32. パフォーマンス
  33. 33. Clickイベントが遅延する問題 • タッチデバイスブラウザはtouchstart後clickイベント までの間隔が300msec程度空く • リンククリックの反応が遅い • もっさり感 • 解決策 • FastClick.js • clickイベントが速くなる、リンククリックも改善 • jQuery mobileであればvclickイベントを使う • clickでは無くtouchendイベントを使う (ブラウザのデバッグが面倒になる)
  34. 34. DOM操作のチューニング • DOM操作は最小限に、変更が必要な箇所のみ • アニメーションはGPUを効かせる • -webkit-transform: translate3d を使う • レイアウトの再計算が発生する回数を減らす • 例 • offsetHeightを取得して挿入した要素の高さだけ スクロール位置を下にずらす処理 • DOM操作全てが終ったタイミングで行なう
  35. 35. 無限リスト • DOMツリーが増え続けると重くなる • iOSのテーブルセルの再利用のような仕組みが必要 になる • iOS SDKのUITabelViewCellの標準機能だけどDOM にはそんな物無いです……
  36. 36. デバッグ
  37. 37. ブラウザでデバッグ可能にする Chromeで開発 and デバッグ ↓ iPhoneシミュレーターで開発 and デバッグ ↓ 実機 (最終確認)
  38. 38. • ブラウザでデバッグする際はネイティブ機能呼び出 しのブリッジを差しかえる (Alert, ActionSheet ...) • AppDelegate相当の物を作っておく • イベント受信部 • Push通知, Navigationbar button操作, on/offline.. • どの画面からでも開始できるようにしておく • 画面遷移して到達しないと機能しない画面はデバ ッグしづらい。なるべくステートレスに。 • Chromeのエミュレーションモード • iPhone5, 4S etc... 選べるがイマイチ ブラウザでデバッグ
  39. 39. Chromeのエミュレートモード
  40. 40. • オートリロード必須 • リロードするのが手間 • grunt auto-reload を使う • JS, HTMLはソース編集と同時にリロード • CSSはDOMの状態を維持したまま変更が反映さ れる • window.onerrorでキャッチした例外をXcodeのロ グに吐くようにしておく(特に発生行数) • Safariの開発メニューから、開発者コンソールを アタッチできる iOSシミュレーターでデバッグ
  41. 41. • ローカルサーバーと通信してると何もかもが一瞬で 取得できてしまう • Network Link Conditioner を使う • 使わないと3G回線の速度を再現できない • 通信中インジケーターをゆっくり眺められる 通信状況のエミュレート
  42. 42. 通信状況のエミュレート
  43. 43. • 特にアニメーション、タッチレスポンス周りを確認 • 通信中の電話着信や、アプリがバックグラウンド に行っても大丈夫か 実機でデバッグ
  44. 44. サーバーAPIの実装
  45. 45. • 素直なRESTful APIにしておく • Request • content-type: application/json • Response • content-type: application/json 設計 モダンなJavaScriptライブラリはこれが前提になって いたりする(ex. backbone.js)。道を外れると一気に工 数が跳ね上がる。
  46. 46. • APIサーバーはCross Domain XHRに対応しておく • HTML, JS, CSSはlocalhost (grunt server)で編集 • APIサーバーはAWS上の開発機を参照してAjaxで 叩く • デザイナー or フロント担当エンジニアはローカ ルに静的ファイルの開発環境のみ作れば良い • 認証情報を独自ヘッダに持たせている場合 • OPTIONSのプリフライトリクエストが飛ぶので APIサーバーをCORSに対応させておく 開発時
  47. 47. まとめ
  48. 48. • とにかくブラウザでデバッグする • UIの実装はネイティブ同様時間がか かる (楽はできない) • 覚悟が必要 まとめ

×