2014-01-30

UIWebViewのスクロール減速率の初期値がUIScrollViewDecelerationRateNormalでは無いのは何故か

iOSのUIWebViewのスクロールの減速率について。人に説明していると何故かややこしい話になってしまうのは初期値がノーマルじゃないせいな気がした。

UIWebViewの慣性スクロールの減速率を決める _webview.scrollView.decelerationRateの値だが、初期値は UIScrollViewDecelerationRateFast (以降RateFastと表記)になっている。これはUITableView等が 
UIScrollViewDecelerationRateNormal (以降RateNormalと表記)となっているのと違って、より早く減速するためスクロールして指を離した後に進む距離が短かい。変更するには
_webview.scrollView.decelerationRate = UIScrollViewDecelerationRateNormal;
とすれば良いが、これはWebViewの内容によって使い分けるのが良いだろう。一覧リストの様に、目的の物が見つかるまでスクロールし続ける画面であれば、よりスムーズにスクロールできる RateNormal 。記事を読ませるような画面であれば、1画面分づつスクロールできれば良いので RateFast といった具合に。

decelerationRateの型はCGFloatなので任意の実数をセットできそうだが、0.1等をセットしても無視されてしまう。ドキュメントにある様に UIScrollViewDecelerationRateNormal と UIScrollViewDecelerationRateFast しか効果が無い。

ちなみにfacebookアプリで外部リンクを踏んだ時のUIWebViewは RateNormal で、SmartNewsだと RateFast になってる模様。どちらも違和感を感じないので、Webの記事を読む時はどちらでも気にならないのかも。

タイトルの問いに対する答えは、AppleはUIWebViewに表示するコンテンツはWebの記事が多いだろうという仮説でもって、記事が読みやすいようにスクロールがすぐに止まるようにしたのでは無いかと。

このエントリーをはてなブックマークに追加

2014-01-19

瞑想システムのフィードバックはどうあるべきか

瞑想を日々の生活に取りいれる事にした。しかし一度も瞑想をした事が無いため、どのようにして瞑想状態に入るか、また瞑想状態に入ったかどうかを判断する方法がわからない。まずはそこからである。

瞑想のやり方はWebを検索するといくらでも見つかるので、それらを参考にする。そして瞑想の計測方法だが、幸い手元に脳波ヘッドセットのEPOC neuroheadsetと開発者SDKがあるので、これを使う。コードを書くまでもなく欲しい物そのものである Meditation という値が付属ツールで表示できる。

上のスクリーンショットは瞑想に初めて挑戦した時の物、Meditaionの値が平常時と同じ = つまり失敗しているのがわかる。瞑想の修行を積む必要がある様だ。

しかし、瞑想に入ろうとしている際にこの Meditation のグラフに目をやると、同時に Instantaneous Excitement の値が跳ね上がり、所謂雑念に気を取られてしまうのがわかる。瞑想できているかどうかを気にした時点で負けかもしれない。なんらかの方法で瞑想状態のフィードバックを送るとしても、それに気をとられないような工夫が必要だろう。
既にある特許で瞑想装置(ミサワホーム株式会社. 瞑想装置. 特開平5-253302. 1993-10-05)があるが、これは背もたれ後方に配置した照明がアルファ波によって変化するとある。製品化されたかは不明だが、今ならhueを使えば簡単に実現できそうだ。

ミサワホーム株式会社. 瞑想装置. 特許公開平05-253302 より

とりあえず今はリアルタイムでフィードバックするのでは無く、Meditation の値の経過を後で参照できれば良いのでGrowthlForecastに投げるコードを書くとする。

このエントリーをはてなブックマークに追加

2014-01-18

vim-python-pep8-indentプラグインで幸福実現した

Pythonのコーディングスタイルチェックにはflake8を使っていたのだが、インデントルールの次の二つは守れないでいた。
E126 continuation line over-indented for hanging indent
E128 continuation line under-indented for visual indent
具体的にはvimのインデント(Visualモードで範囲選択して = )だとpep8のインデントルールになってくれないので、ignore = E128,E126 していた。

そこで、@seizansに教えてもらった vim-python-pep8-indent を入れたらvimのインデントがpep8準拠になり、ほぼノーコストでpep8対応が完了できるようになった。これは良い。

.vimrcの記述は次の通りにした。
" flake8
NeoBundleLazy 'hynek/vim-python-pep8-indent', {
    \ "autoload": {"insert": 1, "filetypes": ["python", "python3", "djangohtml"]}}

このエントリーをはてなブックマークに追加

2013-12-17

iOS WebViewアプリ勉強会の資料と補足 #vgadvent2013

この記事はVOYAGE GROUP エンジニアブログ: Advent Calendar 2013の15日目です。

先日社内で行なった勉強会の資料をSlide Shareにアップしました。口頭で説明した内容を追加してあります。


補遺

前提としてiOSのWebView(ハイブリッド)アプリでネイティブアプリになるべく近いLook and Feelを実現する際の話です。

ネイティブとの連係

直近のプロジェクトではコードベースを最小にしたかったので、PhoneGapを使わなかったのですが、最新のPhoneGapは必要な機能を切り出してビルドして使えるみたいですね。素晴らしい。あまり自分で実装したくない所なので、既存で使える物をなるべく使いましょう。

なるべくブラウザ(Chrome)でデバッグする理由

iPhoneシミュレーター、実機ではデバッグしづらいからです。

touchendとclickイベント

clickイベントが遅延する対策としてtouchendを使っても良いと説明しましたが、touchendイベントとclickイベントは発動条件が異なります。具体的にはタッチした後に別の要素まで指を動かして離すとtouchendはFireするが、clickイベントは飛びません。タップしたけどやっぱ辞めた、という動きはclickイベントを使った方が意図した通りになります。
私はFastClick.jsを有効にして、タッチの処理はclickイベントをハンドルしています。

パフォーマンス周り

最後のまとめで覚悟が必要とさらっと書いてありますが、どういう事かと言うと。ネイティブであればiOS SDKが標準で用意してくれる、少ないメモリ、貧弱なCPUでUIを構築するための仕組み、例えばUITableCellの再利用やGrand Central Dispatch(GCD)がHTML5/JavaScriptには無いです。独自で実装したり、WebWorkerを利用してバックグラウンドプロセスで動かすという手もありますが面倒な事に変りはありません。そのあたりをカバーしつつ、レンダリングエンジンの気持ちになって考え、DOM操作を最適化していくという覚悟が必要、という意味です。Androidに比べればWebViewがサクサク動いてくれる、というのが唯一の救い。
初回リリース時は工数のかかるパフォーマンス、アニメーションのチューニングを潔く諦めるのもアリかなと思ってます。いつでも直せるのがメリットなので。

それ以外に大変な事

やった事はありませんが、オリエンテーション対応が必要という場合はもうSencha Touchを使った方が良いと思います。2.3でiOS 7のスタイルも追加されました。個人的にはjQuery mobileよりもおすすめ。学習コスト高いけど。

Advent Calendar明日の担当は@shinbashiさんです。既に記事が公開済みという手筈の良さ、流石ですね。

このエントリーをはてなブックマークに追加

2013-11-30

wheelを使ってPythonのC拡張モジュールを本番デプロイする

Pythonの話。wheelを使ってC拡張モジュールをデプロイする仕組みが上手く稼動したのでメモ。

依存パッケージの本番デプロイ

アプリケーションが依存しているPythonパッケージをどうやって本番サーバーにデプロイするか。大抵はrequirements.txtにpip freezeで吐いた内容を保存しているだろう。とすると、本番サーバーでpip install -r requirements.txtすれば良いんだが、githubが落ちてたりPyPIが落ちてたりすると、外部要因でデプロイスクリプトが途中でコケる、というダサい事態になる。それを避けるために事前にパッケージを固めて各サーバーに配布する仕組みが必要になる。C拡張モジュールを使いたいけど本番サーバーでCコンパイラが自由に使えない、という時も同様で、事前にコンパイル済みの物を配布する必要がある。

pip bundleを使う場合 (deprecated)

Pure Pythonなパッケージだけを使っている場合はこれで十分だろう。
$ pip bundle -r requirements.txt myproduct.bundle
で、myproduct.bundleを作成しておいて、デプロイ先で
$ pip install myproduct.bundle
とする。しかし、C拡張モジュールがあると、デプロイ先でもりもりコンパイルが始まってしまって非効率なのと、そもそもpip 1.5でpip bundle自体が削除予定なので今後は忘れても良い機能だ。

pip wheel でコンパイル済みC拡張モジュールをデプロイする

これが本命、wheelというフォーマットでパッケージを配布する方式。
私はCIサーバーでwheelを作って本番サーバーに転送して使っている。手順は、まず事前に各サーバーでwheelを導入しておく。
$ pip install --upgrade pip        # 1.4以上が必要
$ pip install --upgrade setuptools # 0.8以上が必要
$ pip install wheel
wheelの作成は次の通り。慣例的にwheelhouseというディレクトリ名が使われる様だ。
# requirements.txtがこんな内容だとする
$ cat requirements.txt
MySQL-python==1.2.4
python-memcached==1.53
boto==2.14.0
simplejson==3.3.1

# wheelの作成
$ pip wheel --wheel-dir=./wheelhouse -r requirments.txt
Downloading/unpacking MySQL-python==1.2.4 (from -r requirements_prod.txt (line 1))
  Downloading MySQL-python-1.2.4.zip (113kB): 113kB downloaded
  Running setup.py egg_info for package MySQL-python
    Downloading http://pypi.python.org/packages/source/d/distribute/distribute-0.6.28.tar.gz
    Extracting in /tmp/tmpKuwtk2
    Now working in /tmp/tmpKuwtk2/distribute-0.6.28
    Building a Distribute egg in /web/httpd_spc/chatparty_api/python/build/MySQL-python
    /web/httpd_spc/chatparty_api/python/build/MySQL-python/distribute-0.6.28-py2.6.egg
Downloading/unpacking python-memcached==1.53 (from -r requirements_prod.txt (line 2))
  Downloading python-memcached-1.53.tar.gz
  Running setup.py egg_info for package python-memcached
    warning: no files found matching '*.rst'
    warning: no files found matching '*.txt'
    warning: no files found matching 'MakeFile'
    warning: no previously-included files matching '*.pyc' found anywhere in distribution
    warning: no previously-included files matching '.gitignore' found anywhere in distribution
    warning: no previously-included files matching '.DS_Store' found anywhere in distribution
Downloading/unpacking boto==2.14.0 (from -r requirements_prod.txt (line 3))
  Downloading boto-2.14.0.tar.gz (1.1MB): 1.1MB downloaded
  Running setup.py egg_info for package boto
    warning: no files found matching 'boto/mturk/test/*.doctest'
    warning: no files found matching 'boto/mturk/test/.gitignore'
Downloading/unpacking simplejson==3.3.1 (from -r requirements_prod.txt (line 4))
  Downloading simplejson-3.3.1.tar.gz (67kB): 67kB downloaded
  Running setup.py egg_info for package simplejson
Building wheels for collected packages: MySQL-python, python-memcached, boto, simplejson
  Running setup.py bdist_wheel for MySQL-python
  Destination directory: /web/httpd_spc/chatparty_api/wheelhouse
  Running setup.py bdist_wheel for python-memcached
  Destination directory: /web/httpd_spc/chatparty_api/wheelhouse
  Running setup.py bdist_wheel for boto
  Destination directory: /web/httpd_spc/chatparty_api/wheelhouse
  Running setup.py bdist_wheel for simplejson
  Destination directory: /web/httpd_spc/chatparty_api/wheelhouse
Successfully built MySQL-python python-memcached boto simplejson
Cleaning up...
生成されたファイル名を見ればわかる様に、wheelはPythonバージョンとアーキテクチャ毎に作られる。
$ ls -l wheelhouse/
boto-2.14.0-py26-none-any.whl
MySQL_python-1.2.4-cp26-none-linux_x86_64.whl
python_memcached-1.53-py26-none-any.whl
simplejson-3.3.1-cp26-none-linux_x86_64.whl
wheelを使ったインストールはpip installでwheelの場所を指定するだけ。一瞬で終って気持ちがいい。依存関係は既に解決済みなので --no-deps オプションを使う。
$ pip install --no-deps wheelhouse/*
Unpacking ./wheelhouse/MySQL_python-1.2.4-cp26-none-linux_x86_64.whl
Unpacking ./wheelhouse/boto-2.14.0-py26-none-any.whl
Unpacking ./wheelhouse/python_memcached-1.53-py26-none-any.whl
Unpacking ./wheelhouse/simplejson-3.3.1-cp26-none-linux_x86_64.whl
Cleaning up...



このエントリーをはてなブックマークに追加