0
iOS WebView App
@hagino3000
2013-12-12
社内的な勉強会 atVOYAGE GROUP
注
Androidの話は無いです
なぜWebView上にアプリを
構築するのか
• 任意のタイミングでアプリケーション
をアップデートしたい
• WebでUI作った方が楽そうだから
• そんな事は無い → あとで死ぬ
実装戦略
実装戦略
• HTML, CSS, JSのリソースをアプリにバン
ドルする or NOT
• Single Page Web App or NOT
• ネイティブUIパーツを使う or NOT
HTML, CSS, JSファイルを
アプリにバンドルするか
• 完全にオフラインで動作させる事が可能
• それ以外メリット無し
Single Page Web Appにするか
• Single Page
• JSの実装量は増える
• なんらかのJSフレームワークを使う
Sencha Touch, AngularJS etc...
• Multi Page
• 作りはシン...
(番外編) Multi UIWebView
• ページ毎にUIWebViewを作る
• ネイティブのトランジションが使える
• 試してない
iOSのUIパーツを使うか
iOSのUIパーツを使うか
• NavigationBar, UIAlertView, UIActionSheet
etc..
• 使った方が楽 (モーダル制御等)
• ネイティブUIを呼び出すブリッジを作る
• ブラウザでデバッグできるように...
UIWebView
UIWebView
iOSVersion Browser Component
iOS 6 iOS Safari 6.x
iOS 7 iOS Safari 7.0
機能比較
http://caniuse.com/#compare=ios_saf+...
コンテンツの配置
UIWebView
UIScrollView
HTML Content
(表示領域)
Navigation BarContentsArea(iOS6)
ContentsArea(iOS7)
document.scrollTop 不可視
不可視
ViewPort
<!-- よくある設定 -->
<meta
name="viewport"
content="width=device-width,
height=device-height,
initial-scale=1.0,
maxim...
スクロールの制御
• Single Page Web App
Height 100%のブロック要素を並べて、
その中でスクロールさせる場合はデフ
ォルトのスクロールが邪魔になる。
(二重スクロール)
• そうでない場合はscrollViewのス...
スクロールの制御
// UIScrollViewのスクロールを無効にする
webView.scrollView.scrollEnabled = NO;
/* CSSでブロック要素の慣性スクロールを有効にする */
article.page{
b...
スクロール速度
scrollView.decelerationRate =
UIScrollViewDecelerationRateNormal;
// デフォルトは UIScrollViewDecelerationRateFast
慣性スクロ...
NavigationBar
• ポジション固定にする場合はネイティブのを
使うと楽
• NavigationBarを半透明にして、背後にもコンテ
ンツを配置したい (iOS7)
• 初期表示でnavbarの下にコンテンツが潜りこ
まないようにs...
position: fixed
• 効かない
• スクロールに引きずられる
• iScroll
• onScrollイベントを監視してposition:fixedを再
現している
• https://github.com/cubiq/iscroll...
テキスト入力
1. <input type=”text”> or <textarea> にフォーカス
2. Keyboard Windowがせり上ってくる
3. document.scrollTopがずれる
• テキスト入力パーツはなるべく画面...
その他よくやる設定
<!-- 電話番号らしき数列をリンクにするのを無効化 -->
<meta name="format-detection" content="telephone=no">
body {
/* 文字列のコピーや選択をできなくする...
ネイティブとの連係
To WebView
NSString *js = @”window.App.hello();”;
[self.webView stringByEvaluationgJavaScriptFromString:js];
// 例:ネイティブからs...
From WebView
1. 独自スキーマを指定してlocationを変更
2. UIWebViewDelegateのshouldStartLoadWithRequest
でフック
3. なにかやる
4. UIWebViewのstringBy...
// JS側の実装例
var transactionId = 0;
function nativeBridge(action, params, callback) {
// 後で呼び出すためにコールバック関数を保持する
var transact...
// Objective-C
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIW...
App.nativeBridge(‘alert’, {
title: “Error”,
message:“Sorry...”,
buttonTitle: “OK”
}, function(){
// UIAlertViewのOKボタンがタップさ...
最初から作っておくと便利
• キーボードWindowの上げ下げ
• Alert
• Confirm
• ActionSheet
• ジェスチャー認識
リクエストのフック
• UIWebViewDelegateのshouldStartLoadWithRequest
⃝ ロケーション変更 (iframe含む)
⃝ リロード (iframe含む)
⃝ フォームサブミット
× XMLHttpRequ...
リクエストのフックの応用
• ロケーション変更時、ドメインによって処理を変
える(ex 自サービスの外に出る場合はSafariで開く)
• 認証情報の付加
• NSMutableURLRequestを操作してcookieや独自
ヘッダを付加する...
パフォーマンス
Clickイベントが遅延する問題
• タッチデバイスブラウザはtouchstart後clickイベント
までの間隔が300msec程度空く
• リンククリックの反応が遅い
• もっさり感
• 解決策
• FastClick.js
• click...
DOM操作のチューニング
• DOM操作は最小限に、変更が必要な箇所のみ
• アニメーションはGPUを効かせる
• -webkit-transform: translate3d を使う
• レイアウトの再計算が発生する回数を減らす
• 例
• ...
無限リスト
• DOMツリーが増え続けると重くなる
• iOSのテーブルセルの再利用のような仕組みが必要
になる
• iOS SDKのUITabelViewCellの標準機能だけどDOM
にはそんな物無いです……
デバッグ
ブラウザでデバッグ可能にする
Chromeで開発 and デバッグ
↓
iPhoneシミュレーターで開発 and デバッグ
↓
実機 (最終確認)
• ブラウザでデバッグする際はネイティブ機能呼び出
しのブリッジを差しかえる (Alert, ActionSheet ...)
• AppDelegate相当の物を作っておく
• イベント受信部
• Push通知, Navigationbar ...
Chromeのエミュレートモード
• オートリロード必須
• リロードするのが手間
• grunt auto-reload を使う
• JS, HTMLはソース編集と同時にリロード
• CSSはDOMの状態を維持したまま変更が反映さ
れる
• window.onerrorでキャ...
• ローカルサーバーと通信してると何もかもが一瞬で
取得できてしまう
• Network Link Conditioner を使う
• 使わないと3G回線の速度を再現できない
• 通信中インジケーターをゆっくり眺められる
通信状況のエミュレート
通信状況のエミュレート
• 特にアニメーション、タッチレスポンス周りを確認
• 通信中の電話着信や、アプリがバックグラウンド
に行っても大丈夫か
実機でデバッグ
サーバーAPIの実装
• 素直なRESTful APIにしておく
• Request
• content-type: application/json
• Response
• content-type: application/json
設計
モダンなJavaSc...
• APIサーバーはCross Domain XHRに対応しておく
• HTML, JS, CSSはlocalhost (grunt server)で編集
• APIサーバーはAWS上の開発機を参照してAjaxで
叩く
• デザイナー or フ...
まとめ
• とにかくブラウザでデバッグする
• UIの実装はネイティブ同様時間がか
かる (楽はできない)
• 覚悟が必要
まとめ
Upcoming SlideShare
Loading in...5
×

iOS WebView App

8,467

Published on

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

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

No Downloads
Views
Total Views
8,467
On Slideshare
0
From Embeds
0
Number of Embeds
19
Actions
Shares
0
Downloads
40
Comments
0
Likes
25
Embeds 0
No embeds

No notes for slide

Transcript of "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の実装はネイティブ同様時間がか かる (楽はできない) • 覚悟が必要 まとめ
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×