48
iOS WebView App @hagino3000 2013-12-12 社内的な勉強会 at VOYAGE GROUP

iOS WebView App

  • View
    5.778

  • Download
    7

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: iOS WebView App

iOS WebView App@hagino3000

2013-12-12

社内的な勉強会 at VOYAGE GROUP

Page 2: iOS WebView App

注Androidの話は無いです

Page 3: iOS WebView App

なぜWebView上にアプリを構築するのか

• 任意のタイミングでアプリケーションをアップデートしたい

• WebでUI作った方が楽そうだから

• そんな事は無い → あとで死ぬ

Page 4: iOS WebView App

実装戦略

Page 5: iOS WebView App

実装戦略

• HTML, CSS, JSのリソースをアプリにバンドルする or NOT

• Single Page Web App or NOT

• ネイティブUIパーツを使う or NOT

Page 6: iOS WebView App

HTML, CSS, JSファイルをアプリにバンドルするか

• 完全にオフラインで動作させる事が可能

• それ以外メリット無し

Page 7: iOS WebView App

Single Page Web Appにするか

• Single Page

• JSの実装量は増える

• なんらかのJSフレームワークを使うSencha Touch, AngularJS etc...

• Multi Page

• 作りはシンプルになる

Page 8: iOS WebView App

(番外編) Multi UIWebView

• ページ毎にUIWebViewを作る

• ネイティブのトランジションが使える

• 試してない

Page 9: iOS WebView App

iOSのUIパーツを使うか

Page 10: iOS WebView App

iOSのUIパーツを使うか

• NavigationBar, UIAlertView, UIActionSheet etc..

• 使った方が楽 (モーダル制御等)

• ネイティブUIを呼び出すブリッジを作る

• ブラウザでデバッグできるようにPolyfillを用意しておく

Page 11: iOS WebView App

UIWebView

Page 12: iOS WebView App

UIWebView

iOS Version 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

Page 13: iOS WebView App

コンテンツの配置

Page 14: iOS WebView App

UIWebView

UIScrollView

HTML Content(表示領域)

Navigation Bar

Contents A

rea (iOS6)

Contents A

rea (iOS7)

document.scrollTop 不可視

不可視

Page 15: iOS WebView App

ViewPort

<!-- よくある設定 --><meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0"/>

Page 16: iOS WebView App

スクロールの制御

• Single Page Web AppHeight 100%のブロック要素を並べて、その中でスクロールさせる場合はデフォルトのスクロールが邪魔になる。(二重スクロール)

• そうでない場合はscrollViewのスクロールを使う

Page 17: iOS WebView App

スクロールの制御

// 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;}

Page 18: iOS WebView App

スクロール速度scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;

// デフォルトは UIScrollViewDecelerationRateFast

慣性スクロールの減速が緩くなる。scrollViewのスクロールを使う場合は設定する。CSSでブロック要素の慣性スクロールを有効にした場合は UIScrollViewRateNormal 相当になる。

Page 19: iOS WebView App

NavigationBar

• ポジション固定にする場合はネイティブのを使うと楽

• NavigationBarを半透明にして、背後にもコンテンツを配置したい (iOS7)

• 初期表示でnavbarの下にコンテンツが潜りこまないようにscrollViewの調節が必要

• webView.scrollView.contentInset

• webView.scrollView.contentOffset

• webView.scrollView.scrollIndicatorInsets

Page 20: iOS WebView App

position: fixed

• 効かない• スクロールに引きずられる

• iScroll

• onScrollイベントを監視してposition:fixedを再現している

• https://github.com/cubiq/iscroll

• 代替策として別のViewにして配置するのもアリ

Page 21: iOS WebView App

テキスト入力1. <input type=”text”> or <textarea> にフォーカス2. Keyboard Windowがせり上ってくる3. document.scrollTopがずれる

• テキスト入力パーツはなるべく画面上部に配置する。下の方だと最悪Keyboard Windowが被る。

• scrollViewのスクロールを無効にしているとユーザーが元に戻せない。→入力後にscrollTopを調整する。

• JavaScriptからキーボードの上げ下げを可能にしておく

Page 22: iOS WebView App

その他よくやる設定<!-- 電話番号らしき数列をリンクにするのを無効化 --><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);}

Page 23: iOS WebView App

ネイティブとの連係

Page 24: iOS WebView App

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];

Page 25: iOS WebView App

From WebView

1. 独自スキーマを指定してlocationを変更2. UIWebViewDelegateのshouldStartLoadWithRequestでフック

3. なにかやる4. UIWebViewのstringByEvaluatingJavaScriptFromStringで結果を返す

ヒント:PhoneGapのソースの方がこのスライドよりも参考になるhttps://github.com/phonegap/phonegap/blob/master/lib/ios/CordovaLib/cordova.js

Page 26: iOS WebView App

// 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();}

Page 27: iOS WebView App

// 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;}

Page 28: iOS WebView App

App.nativeBridge(‘alert’, { title: “Error”, message:“Sorry...”, buttonTitle: “OK”}, function(){ // UIAlertViewのOKボタンがタップされた})

使用例

Page 29: iOS WebView App

最初から作っておくと便利

• キーボードWindowの上げ下げ• Alert• Confirm• ActionSheet• ジェスチャー認識

Page 30: iOS WebView App

リクエストのフック

• UIWebViewDelegateのshouldStartLoadWithRequest

◯ ロケーション変更 (iframe含む)

◯ リロード (iframe含む)

◯ フォームサブミット× XMLHttpRequest× script要素、link要素、img要素による通信

• NSURLProtocol• 全ての通信をフックできる、ホワイトリストを実装するならこちら

Page 31: iOS WebView App

リクエストのフックの応用

• ロケーション変更時、ドメインによって処理を変える(ex 自サービスの外に出る場合はSafariで開く)

• 認証情報の付加• NSMutableURLRequestを操作してcookieや独自ヘッダを付加する

• ホワイトリスト (NSURLProtocol)

• しかし、アドネットワークを利用して広告を表示しているとホワイトリストのメンテは困難を極める

Page 32: iOS WebView App

パフォーマンス

Page 33: iOS WebView App

Clickイベントが遅延する問題• タッチデバイスブラウザはtouchstart後clickイベントまでの間隔が300msec程度空く

• リンククリックの反応が遅い• もっさり感

•解決策• FastClick.js

• clickイベントが速くなる、リンククリックも改善• jQuery mobileであればvclickイベントを使う• clickでは無くtouchendイベントを使う

(ブラウザのデバッグが面倒になる)

Page 34: iOS WebView App

DOM操作のチューニング

• DOM操作は最小限に、変更が必要な箇所のみ• アニメーションはGPUを効かせる

• -webkit-transform: translate3d を使う• レイアウトの再計算が発生する回数を減らす

• 例• offsetHeightを取得して挿入した要素の高さだけスクロール位置を下にずらす処理

• DOM操作全てが終ったタイミングで行なう

Page 35: iOS WebView App

無限リスト

• DOMツリーが増え続けると重くなる• iOSのテーブルセルの再利用のような仕組みが必要になる• iOS SDKのUITabelViewCellの標準機能だけどDOMにはそんな物無いです……

Page 36: iOS WebView App

デバッグ

Page 37: iOS WebView App

ブラウザでデバッグ可能にする

Chromeで開発 and デバッグ↓iPhoneシミュレーターで開発 and デバッグ↓実機 (最終確認)

Page 38: iOS WebView App

• ブラウザでデバッグする際はネイティブ機能呼び出しのブリッジを差しかえる (Alert, ActionSheet ...)

• AppDelegate相当の物を作っておく• イベント受信部• Push通知, Navigationbar button操作, on/offline..

• どの画面からでも開始できるようにしておく• 画面遷移して到達しないと機能しない画面はデバッグしづらい。なるべくステートレスに。

• Chromeのエミュレーションモード• iPhone5, 4S etc... 選べるがイマイチ

ブラウザでデバッグ

Page 39: iOS WebView App

Chromeのエミュレートモード

Page 40: iOS WebView App

• オートリロード必須• リロードするのが手間• grunt auto-reload を使う• JS, HTMLはソース編集と同時にリロード• CSSはDOMの状態を維持したまま変更が反映される

• window.onerrorでキャッチした例外をXcodeのログに吐くようにしておく(特に発生行数)

• Safariの開発メニューから、開発者コンソールをアタッチできる

iOSシミュレーターでデバッグ

Page 41: iOS WebView App

• ローカルサーバーと通信してると何もかもが一瞬で取得できてしまう

• Network Link Conditioner を使う

• 使わないと3G回線の速度を再現できない• 通信中インジケーターをゆっくり眺められる

通信状況のエミュレート

Page 42: iOS WebView App

通信状況のエミュレート

Page 43: iOS WebView App

• 特にアニメーション、タッチレスポンス周りを確認• 通信中の電話着信や、アプリがバックグラウンドに行っても大丈夫か

実機でデバッグ

Page 44: iOS WebView App

サーバーAPIの実装

Page 45: iOS WebView App

• 素直なRESTful APIにしておく• Request

• content-type: application/json• Response

• content-type: application/json

設計

モダンなJavaScriptライブラリはこれが前提になっていたりする(ex. backbone.js)。道を外れると一気に工数が跳ね上がる。

Page 46: iOS WebView App

• APIサーバーはCross Domain XHRに対応しておく• HTML, JS, CSSはlocalhost (grunt server)で編集

• APIサーバーはAWS上の開発機を参照してAjaxで叩く

• デザイナー or フロント担当エンジニアはローカルに静的ファイルの開発環境のみ作れば良い

• 認証情報を独自ヘッダに持たせている場合

• OPTIONSのプリフライトリクエストが飛ぶのでAPIサーバーをCORSに対応させておく

開発時

Page 47: iOS WebView App

まとめ

Page 48: iOS WebView App

• とにかくブラウザでデバッグする• UIの実装はネイティブ同様時間がかかる (楽はできない)

• 覚悟が必要

まとめ