SKIPのiPhone用ビューを作ってみたよ。
はじめに
冬休みの宿題でSKIPのiPhone対応してみると手をあげてはみたもののサボってしまったので、この三連休に慌てて作ってみたというお話。
まずは学習
ちょっと古い記事だけど以下を見て基本を学習
http://www.atmarkit.co.jp/fwcr/special/iphone/02.html
http://www.atmarkit.co.jp/fwcr/special/iphone/03.html
http://www.atmarkit.co.jp/fwcr/special/iphone/04.html
重要っぽい所を抜き出すと以下のような感じだった。
02.html Web標準に対応している 未対応技術がある Webブラウザ判定には2種類ある UAでの判定 Apple提供のWeb Kit 判定スクリプト(Detecting WebKit with JavaScript ダブルタップによるズーム img -> 画像の横幅一杯にズーム 画像以外 -> その要素を包含するブロック要素の横幅一杯にズーム リソース制限 HTML CSS JavaScriptなどは10Mbyteまで JavaScriptの実行時間は[5sec]まで!!!!!! JavaScriptのアロケーションは10Mbyteまで -> アロケーションって何だっけ 03.html ビューポートの設定 ポートレート表示(縦表示)だと320x480(320x356) ランドスケープ表示(横表示)だと480x320 つまり320x356がiPhone版Safariに最適化されたデザイン!! ただし、デフォルトで縮小表示(横幅を980px)として表示される…のか -> そこでビューポートメタタグですよ -> device-widthという便利な値が指定出来るよ(勝手に横幅320pxにしてくれる 04.html スタイルシート CSS3プロパティの先行サポート webkit-border-radius(角丸) 文字の拡大縮小(自動アジャスト) トランジションとアニメーション js使わなくても色々な効果が使えるみたいだ iPhoneボタンとiPhoneリストパターン フォーム要素はiPhone独自のUI部品にアクセス可 Web Apps Dev Center iPhoneらしい各種ボタン iPhoneらしいリスと表示 などを実現するCSSを提供している。 -> SDKでなくともネイティブっぽい見た目にできる。 入力イベント ドラッグ ドラッグ中にonscrollが発生しない 指を止めてドラッグを終了した時点でonscrollが発生 タッチ&ホールド マウスイベントなし ダブルタップ マウスイベントなし タップ クリック不可要素では発生しない クリック可能要素ではmousemoveイベントが発生 コンテンツが変化しない場合はmousedown, mouseup, clickが発生 mousemoveはmouseover, mouseoutも発生させる 2本指ドラッグ スクロール可能な要素の場合mousewheel それ以外の場所では1本指ドラッグと同様 デバッグ iPhone版Safariには簡単なデバッグモードがある。 -> 本体上でスクリプトエラーを確認可能 デスクトップ版Safariの開発メニューで詳細な動作確認が可能 MacがあればiPhoneシュミレータが使える…
jsの実行時間5秒までってのは要注意だなーと思った。
要件を考える。
ブレスト iPhoneSKIPで使いたい機能って何だろう?(自分が 未読記事のチェック 未読コメントのチェック コメントを加える 上記ぐらいが出来れば十分だよなぁ。最初は読めるだけでいいや。 要件 ログイン画面 ログインするとマイページに飛ぶ yahooのように機能ごとのアイコンが並んだ画面がいいかなぁ? 機能が階層化された一覧画面ぽいのがいいかなぁ? 何を表示して何を表示しないのかが重要 最初は面倒なんでログインすると未読記事画面でいいや。 未読記事画面 320x356の画面である PCサイトの未読記事のボックスのみが表示されるイメージ 右側に [>] が表示されており、クリックすると記事一覧画面に遷移する。 記事一覧画面 タイトル、本文をメールアプリのように色や文字の大きさで表現する。 右側に [>] が表示されており、クリックすると記事画面に遷移する。 記事画面
ライブラリの調査
軽くググると
http://d.hatena.ne.jp/fujisan3776/20080902/1220355322
http://d.hatena.ne.jp/fujisan3776/20080903/1220452741
こんな記事を見つけたので、jpmobileとiUIを導入することに決定。
準備
GitHubでfork済みの自分のskipリポジトリをcloneしてくる。
git clone git@github.com:maedana/skip.git maedana_skip
iPhone開発用のブランチを作ってチェックアウト
cd maedana_skip git checkout -b for_iphone master
jpmobileは現時点(09/01/12)でiPhoneにはまだ対応してない(議論はしてて話は進んでいる模様)ので
上述の記事にしたがってjpmobileにちょっとコードを追加したものを導入。
追加したコードの差分は以下のとおり。
http://github.com/maedana/jpmobile/commit/876bcc64f9c06298aa39e0ee8af5d83f20034cfe
iUIは以下をDLして導入。
http://iui.googlecode.com/files/iui-0.13.tar.gz
実装
iUI同梱のサンプルがわかりやすいので基本的に詰まる部分はなかったが2ヶ所はまった。
既存のRailsのcontrollerを流用する際にハマる
LunchController#list から #detailへ遷移する際、application_mobile_iphone.rbが読み込まれると、HTMLのヘッダなど余分なものがくっついてしまい、レイアウトがおかしくなります。そのため、#detailのrender時、iPhoneからのアクセスの時はlayout => false にして、application_mobile_iphone.rbが読み込まれないようにする必要があります。
http://d.hatena.ne.jp/fujisan3776/20080902/1220355322
とあるように、既存のRailsのcontrollerを流用する場合はlayoutをfalseになるようにしてあげないとダメ。
既存のviewを流用する際のAjaxが動かなくてハマる(未解決)
iUIでは外部ページ(リンク)を取得する際にAjaxで取得するんだけど、その際にコールバックが外部から指定出来ない?みたい。
コールバックが指定できればその中で既存のAjax処理を呼ぶようにオーバーライドしてあげればいいと思うんだけど。
具体的には以下の部分
http://code.google.com/p/iui/source/browse/trunk/iui/iui.js#65
showPageByHref: function(href, args, method, replace, cb) { var req = new XMLHttpRequest(); req.onerror = function() { if (cb) cb(false); }; req.onreadystatechange = function() { if (req.readyState == 4) { if (replace) replaceElementWithSource(replace, req.responseText); else { var frag = document.createElement("div"); frag.innerHTML = req.responseText; iui.insertPages(frag.childNodes); } if (cb) setTimeout(cb, 1000, true); } };
showPageByHrefの第5引数のcbでコールバック関数(処理成功時はcb(true), 失敗時はcb(false)を呼ぶようになっているが…
http://code.google.com/p/iui/source/browse/trunk/iui/iui.js#176
addEventListener("click", function(event) { var link = findParent(event.target, "a"); if (link) { function unselect() { link.removeAttribute("selected"); } if (link.href && link.hash && link.hash != "#") { link.setAttribute("selected", "true"); iui.showPage($(link.hash.substr(1))); setTimeout(unselect, 500); } else if (link == $("backButton")) history.back(); else if (link.getAttribute("type") == "submit") submitForm(findParent(link, "form")); else if (link.getAttribute("type") == "cancel") cancelDialog(findParent(link, "form")); else if (link.target == "_replace") { link.setAttribute("selected", "progress"); iui.showPageByHref(link.href, null, null, link, unselect); } else if (iui.isNativeUrl(link.href)) { return; } else if (!link.target) { link.setAttribute("selected", "progress"); iui.showPageByHref(link.href, null, null, null, unselect); } else return;
showPagebyHrefを呼ぶ際にコールバック関数としてunselect関数を固定で渡してるんだよね。
コールバック関数に指定する関数を外部からオーバーライドできればいいと思うんだけどこのままじゃ出来ない…よね?(jsは苦手なので自信ない…)
なので、ちょっと改造していっそのことjQueryプラグイン化してしまおうと思ってる。(まだやってない。)
そんなこんなで以下のような感じのものが出来た
ログイン画面
メニュー画面(マイページ)
記事一覧画面
記事画面(コメントを書いたり、コメントに返信したり、コメントを並び替えたりといった処理をjQueryで行っているが今は動かない。(
ソース
出来上がったソースは以下においてある。
http://github.com/maedana/skip/tree/for_iphone
本体に取り込めるかどうかは…チームの意見を聞いてみないとわからない。
気がついたこととか
- SKIP本体のmypage周辺のcontrollerの処理のいまいちな点に引きずられた実装になっている
- mypage#indexがリクエストパラメタによっていろんな仕事やりすぎな点
- user#blogとgroup/bbsが似すぎている点(モジュールで共通処理を切り出したほうがいい)
- iUIが外部ページをAjaxで読み込む際にコールバック関数を外部から指定できないため、コメント関連のAjaxを効かせられない。
- iphone用cssを書きたいが、SKIP本体のhtml及びcssの整理を先にやったほうがいい。
- style属性への直接指定をなくす
- idやclass名をわかりやすいシンプルな名前にする。
- divの使い方がひどい。なんでもdivにすればいいってものじゃないよなぁ。
- 手元の環境だとjpmobileのspecが動かせなかった。原因はつかめなかった。