Thursday, July 14, 2011

iOS autoresizing UIView TIPS

2011-11-26: 根本的に理解が足りない気がしてきた。 autoresizingMask や layoutSubviews を使えば済む場合が多いはずなので整理ができたらまとめます。
2011-10-04: view controller を window に直接 addSubview するのでなく UINavigationController や UITabBarController 配下にするばあいは viewWillAppear でも orientation は反映されているようなので、あとで調べて情報を更新したい
2011-08-05: orientation が landscape の場合には - didRotateFromInterfaceOrientation: まで待つ必要があったので修正

InterfaceBuilder を使わずにコードで iOS の view 階層を作成して画面サイズや orientation にあわせて autoresize させるにはいろいろと注意が必要になるのですが、イマイチ情報が無かったのでわかったことを書いておきます。

ポイントとしては、 Apple のドキュメント "View Controller Programming Guide for iPhone OS" (rev. 2010-05-03) の "Custom View Controllers" だと、コードで view 階層を作成するには loadView でやれみたいに読めるのですが、 loadView や viewDidLoad のタイミングでは autoresize はまだ反映されず、 viewWillAppear のタイミングで反映されるようです。
さらに、 orientation が landscape (ヨコ) の場合には viewWillAppear ではダメで didRotateFromInterfaceOrientation: まで待たないとダメなようです。

ちなみに、 orientation が viewController.view に反映されているかどうかは、 view.transform に rotation の値が入ってるかどうかで分かるようです。
<UIView: 0x5b59d80; frame = (20 0; 748 1024); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0x5b532a0>>


従って、画面のサイズ(iPhone or iPad)、 navigation bar などの表示の有無 によって autoresize される view の frame や bounds に反映されるのは viewWillAppear のタイミングなので、例えば、それらの autoresize される view の subviews として view をグリッド状に並べたりするのは viewWillAppear でやれってことになります (orientation が portrait でないときは didRotateFromInterfaceOrientation)。

ちなみに、 loadView で生成して addSubview する view をおまかせで画面に fit させる場合、
initWithFrame する必要は無く、 frame は (0 0; 0 0) で OKのようです。
- (void)laodView
{
    UIView *rootView = [[[UIView alloc] init] autorelease];
    rootView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
    self.view = rootView;
}


逆に fit させる大きさ(この場合、navigation bar などを除いたサイズ) より大きい frame を設定してしまうと autoresize はうまくゆかないようです。

また、 [super loadView] で自動生成される view は [[UIView alloc] init] した view とは違い、そのタイミングでの (orientation の反映されていない) frame になっていたり、プロパティの値が違うのか、期待どおりの動きにするのが面倒な感じなので自前で [[UIView alloc] init] したほうがよさそうです。

Sunday, May 22, 2011

Rails 3.1 HTTP Streaming and content_for in controller

2012-01-14: content_for_in_controllers を gem にしてくれた人がいます。 thanks!

Rails 3.1 では HTTP Streaming が導入されますが、 stream ON だと template と layout の処理順序がいままでの rails のように template -> layout ではなく layout 前半 -> template -> layout 後半 という自然な流れになるのですが、 content_for が絡むとややこしいことになります (詳しくは action_controller/metal/streaming.rb のコメントを参照)。

この関係で、 layout 前半で content_for :bar が先に実行されていることを期待した yield :bar が空振りしないように、 layout 前半で yield :bar などが実行されると stream であるのにも係わらず template が先に実行されます。このジレンマを緩和するために provide というヘルパーが導入されて、 provide :bar されると、以降の template の処理を中断して layout 前半を処理しているようです。

もうこうなると stream ON のときは content_for は使わないで、 controller で @title = "foo" とかやってしまいそうになるので、 content_for を controller でも実行できるようにしてみました。 rails 2.X, 3.0 で使っていたやっつけ実装なのですがいちおう rails 3.1 でも動くことは確認しました。




みたいな感じで使います。

あと HTTP Streaming に関しては、
いままでは view のベンチマーク結果に DB の負荷が混ざらないためにも DB アクセスは action 内で実行されるようにしてたけど stream の場合 view のレンダリング中に DB アクセスが実行されるようにしたほうがよいことになりますね。(ソースのコメントにもかいてありますけど。)

また、HTTP Streaming は ruby 1.9 の Fiber が使われているので、 Encoding の問題が煩わしくて ruby 1.9 にする必要性が感じられない場合に ruby 1.9 導入のきっかけになるといいですね。

Monday, May 2, 2011

processing png

2012-02-02: processing.js へのリンクが切れて画像が生成されなかった問題を修正

web や iOS app の開発をしているときにボタンとかに使うチマチマした画像が必要なことがあります。いままでは Seashore でドットを打ったりしてましたが、 Processing.js を使うとプログラムで簡単に絵を描くことができるので、プログラマとしては色やサイズの微調整とかの応用がしやすいですね。

以下は iframe に展開したデモです。 m とか s の値を変えて update すると更新されるのがわかるかと思います。


基本的には Processing.js Web IDEの簡易版ですが、 canvas の内容を img タグに画像としてコピーしているので、その画像をファイルとして保存すれば素材として使えます。

現状、ワークフロー的にはローカルリポジトリのファイルとブラウザ上の textarea, img との手作業のコピペが何度も発生するので洗練されてませんが、工夫すればもうちょっとマシになるかもしれません。

ソースのhtmlは gist にあります (raw を iframe の src に指定すると text/plain になっちゃってダメなので dropbox のファイルを参照)

Tuesday, April 19, 2011

goo.gl Automator service

Mac で選択したテキストのURL短縮できると便利だと思って、調べてみると bit.ly の API を使って Apple Script で実装されたやつは見つかったんですけど、 API key が必要なのは面倒だし Apple Script はいやなので goo.gl を利用するサービスを shell と ruby で書いてみました。



このサービスは選択したテキスト(URL)を短縮して置換します。元のURLは念のためにクリップボードに保存します。
テキストを置換するので、編集可能なテキストしかダメなので、逆に短縮 URL をクリップボードに保存したい人は改造してください。

Automator で作ったサービスは裏で "/System/Library/CoreServices/Automator Runnder.app" を起動して実行するんですけど、コイツの起動が遅いのでメニューバーに常駐する app がサービスを提供する感じにしたほうが速くなると思うのでそのうちに...。

goo.gl の API は ApiKey 無しではあんまり頻繁に実行するとダメかもしれないので、ヘビーに使う人は goo.gl API の詳細: Getting Started - Google URL Shortener API - Google Code を見て自分の ApiKey を設定してみたりしてください。

ちなみにこの記事のコード部分には gist を使ってるんですけど、gist への投稿もサービスでできたら(たぶん使わないけど)便利だと思いましたが、 gist を作成する API は Coming soon だそうです (2011-04-19 現在)。

Friday, April 15, 2011

alias_method_chain for Objective-C

Objective-C の runtime でも ruby のようにメソッドの入れ替えなどを動的に実行できるんですけどいろいろと面倒なので、 alias_method_chain の Objective-C 版を書いてみました。

Wednesday, March 23, 2011

Iodine-131 in tap water in tokyo

ついに東京の水道水にもヨウ素131が混入するようになったそうなので怖くなってリスクをざっくりと計算してみた。

水道水の放射能測定結果について / 第17報 / 平成23年3月23日 / 東京都水道局
金町浄水場: 2011/3/22 9時 I-131 210 Bq/kg
緊急時における食品の放射能測定マニュアル / 平成14年3月 / 厚生労働省医薬局食品保健部監視安全課
I-131(ヨウ化メチル以外の化合物) を幼児が経口摂取した場合の実効線量係数 0.000075 mSv/Bq

ベクレルをシーベルトに換算すると
210Bq/kg * 0.000075mSv/Bq = 0.01575mSv/kg

シーベルト - Wikipedia
これ以上はヤバイかなという値を 100mSv とすると
100mSv / 0.01575mSv/kg = 6,349.2kg (およそ 6t)

一日1L(1kg)水道水を飲んだりすると
6349.2kg / 365日 = 17.39年 で 100mSv に到達

まあ、とりあえずはドキドキしながら暮らすとして、
今後数ヶ月にわたって改善されないと引っ越したくなりますね。

免責条項
- 浄水場から蛇口までの話はわかりません。
- ヨウ素の半減期8日についても考慮してません。
- セシウム134やセシウム137についてはどうなんでしょう。
- 当然ですが、計算間違いの可能性、または仮定の間違いなどあるかと思うので、気になる人は検算してください。

Monday, March 7, 2011

use transactional_fixtures with capybara akephalos driver

You may turn on transactional fixtures with Capybara akephalos driver (it may work with selenium driver too) having the monkey-patch below in spec_helper.rb or somewhere else.
akephalos driver を使っている場合でも以下のコードを spec_helper.rb にでも書いておくと transactional_fixtures を有効にできそうです。 (たぶん selenium などでも同様)



ActiveRecord::ConnectionAdapters::ConnectionPool.class_eval do
def current_connection_id
# Thread.current.object_id
Thread.main.object_id
end
end

かなり無茶苦茶な変更なので ActiveRecord を非同期に使うコードがあるとアウトなので要注意。

以下は簡単な説明です。

Capybara はデフォルトではサーバーも仮想ブラウザもテストを実行している ruby のプロセス内で同期的に処理するので ActiveRecord の connection をテストとサーバーで共有でき、 transactional_fixtures が使えます。

しかし、 Selenium driver の場合ブラウザは本物、 Akephalos は HtmlUnit という Java の仮想ブラウザを別プロセスで実行しています。
そのため、テストのサーバーも実際の http リクエストを処理する必要があるので、 Capybara は thin や webrick を別スレッドで実行することにより対処しています。

一方、 ActiveRecord の ConnectionPool は connection のレースコンデションを避けるためにスレッド毎に異なる connection を返すため、 fixtures ロードを含むテストとサーバーは同一トランザクションにはなりません。

そこで、無理矢理に異なるスレッドでも ConnectionPool が同じ connection を返すように変更。真面目にこの問題に対応するためには、別スレッドでサーバーを起動せずにリクエストをブロックして rack-test で処理すればできそうな気も。