プログラミング: 2010年7月アーカイブ

 iOSでは写真アルバムからの画像を選び出す手段としてイメージピッカー UIImagePickerControllerクラスを提供しています。

 iPad上では,これをPopoverというUIで使用しなさいという条件が付いています。ボタンを押すと現れる吹き出しのようなインターフェイスがPopoverです。

 Popoverの横幅は320ピクセルとされていて,これはiPhoneの横幅と同じなので,テーブルビューなどをそのまま持ってきてPopover内に表示することができます。Popover内がiPhoneアプリのように見えるのも当然です。

--

 ところで,イメージピッカーで写真を選択する場合,範囲選択の編集をさせることができます。allowsEditingプロパティをYESにすれば,選択後に拡大縮小と範囲を移動させる画面が用意されます。

 そして,ピックアップした画像をおさめたNSDictionary型からUIImagePickerControllerEditedImageをキーとして編集結果を取り出すという手順になります。ちなみに,UIImagePickerControllerOriginalImageをキーにすると編集されてないオリジナル画像が取り出せます。

 ところが,この方法を素朴に採用すると横幅最大320ピクセルの画像しか取り出せず,それを拡大するとぼやけてしまいます。

 というのもイメージピッカーがPopoverの中でしか使えないためです。Popoverの大きさを拡大すれば,原理的にはイメージピッカーの切り抜きサイズも大きくなるはずですが,残念ながらPopoverは,縦方向はともかく,横方向は320ピクセルに制限されているため,数値を大きく指定して引き伸ばしてもすぐに幅320ピクセルへ縮んでしまいます。

--

 iPadでイメージピッカーを使用して,大きなサイズの画像を切り抜けないのかというと,そうでもありません。

 幸い,イメージピッカーはオリジナル画像とともに切り抜こうとした矩形範囲の座標をUIImagePickerControllerCropRectキーで教えてくれるのです。

 であれば,こちらでオリジナル画像を切り抜けば良いだけのこと。画像の切り抜き方法は,ググるとあれこれ出てきますので,それを参考にしましょう。

 ところが,うまく切り抜いてアプリも動いているにもかかわらず,処理を繰り返していると頻繁に「落ちる」。メディアを自在に閲覧できるiPadがどうして?と訝しく思えるのですが,どうも画像を保持するUIImageViewでは1024ピクセル以上の画像を扱わないで欲しいらしい。かなりメモリにシビアなのが現状です。

 というわけで,切り抜いた画像を1024ピクセル以内にリサイズすることにして,やっと問題が解消しそうです。

 新しいアプリの開発を始めて,iPadとiPhoneのユニバーサルアプリを勉強しています。Appleの「iPadプログラミングガイド」や『iPadプログラミングの作法』などが参考になります。

 ユニバーサルアプリのメリットはそれほど大きくありません。iPadとiPhoneの個別修正があった場合,変更無い方も一緒に更新作業が必要になる煩雑さがユーザーにはあるかもしれませんし,有料アプリの場合には価格設定を分けられないというのも困るかもしれません。

 開発の手間も減るわけではありませんが,配信手続きが一回で済む点は便利かなと思います。なにしろ,あのAppStoreですから,願わくはシンプルに済ませたいものです。

--

 現時点で,ユニバーサルアプリの開発は,iPhone OS3.2(iPad)とiOS4(iPhone)というメジャーバージョンの異なるOSを想定して作業することになります。

 iOS4からマルチタスクやアプリ切り替え機能が追加されたので,アプリのコア設計も変わってしまっています。その上,iPadとiPhoneは画面解像度が違いますから,UIデザインは二重作業です。

 ところでiPhone3G(S)からiPhone4では解像度が縦横2倍に変わりましたが,この場合のグラフィック処理も2倍で考えるのかと思っていましたが,実はiPhone4においてもプログラミング段階では320x480という座標値でプログラムを作ることになります。

 iPhone4ではそれが高解像度に自動変換されるということが売りのようです。ただし,アプリ内で使用する画像ファイルなどは,2倍の大きさで作り込んでおかないと,低解像度がそのまま引き延ばされた格好になるので見栄えしないというわけです。

 というわけで,新しいiOSでは,プログラミングの手間が増えるというよりも,グラフィック素材の準備に手間がかかるようになっています。どちらかといえば絵心が無くてプログラミングをしているような人間には,悩ましさが増している今日この頃です。

 CoreDataにストアしてあるデータをTableViewでセクションとインデックス付きで表示する際,CoreDataからセクション名を取得する方法があります。

 私が作っているアプリは主に電車の時刻表を扱うので,始発から終電までの発車時刻を並べて表示しています。その際,1時間毎にセクションにまとめて表示します。

 セクション名は,始発が朝5時で終電が夜中1時まであれば,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,0,1という順番になることを意図しています(最後の方が24時,25時でなく0時,1時ってところがミソです)。

 最後の0,1は最初の5,6...よりも小さい数字ですが,データ取得する際のソート指定において内部的に24,26になるようにしてあるわけです。

--

 さて,

 セクション名を取得する方法としてNSFetchedResultsControllerに対してsectionsメソッドでセクションの集合を取り出し,ひとつひとつセクションを取り出して名前を取得していました。

 iPhone OS3.1.3までは,この方法で意図した順番にセクション名を取得できました。ところがiOS4上では,全く同じプログラムなのに取得されたセクション名の並び順が0,1,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23となってしまう挙動に変わってしまっています。

 最初は開発環境の変化で,諸々の設定が適切でなくなったせいなのかと,あれこれ設定を変えてみたのですが,現象に変化はなし。シミュレーターでも実機でもiPhone OS3.1.3では従来通り意図通り動作しているのですが,iOS4では並びが変わってしまいます。

--

■実験

 とにかく実験してみましょう...

○取得方法その1:セクション自体の集合を取得してひとつずつ名前を取り出す

NSArray* fetchedTitles1 = [fetchedResultsController sections];
for (id section in fetchedTitles1) {
NSLog(@"Sec %@",[section name]);
}

○取得方法その2:セクション名の配列を取得して名前を取り出す

NSArray* fetchedTitles2 = [fetchedResultsController sectionIndexTitles];
for (NSString *secname in fetchedTitles2) {
NSLog(@"SecNm: %@",secname);
}


 取得方法その2は,1文字しか取得してくれないので,10番台は「1」20番台は「2」にまとめられて表示されます。
 なお,今回の実験のデータは終電が0時台の時刻表を使用したので,0時だけが注目点になります。

■結果

【iPhone3G + iPhone OS3.1.3】意図した並び...

○取得方法その1
2010-07-17 03:52:31.996 RideOnTime[78:207] Sec 5
2010-07-17 03:52:32.002 RideOnTime[78:207] Sec 6
2010-07-17 03:52:32.007 RideOnTime[78:207] Sec 7
2010-07-17 03:52:32.013 RideOnTime[78:207] Sec 8
2010-07-17 03:52:32.019 RideOnTime[78:207] Sec 9
2010-07-17 03:52:32.024 RideOnTime[78:207] Sec 10
2010-07-17 03:52:32.030 RideOnTime[78:207] Sec 11
2010-07-17 03:52:32.044 RideOnTime[78:207] Sec 12
2010-07-17 03:52:32.050 RideOnTime[78:207] Sec 13
2010-07-17 03:52:32.055 RideOnTime[78:207] Sec 14
2010-07-17 03:52:32.060 RideOnTime[78:207] Sec 15
2010-07-17 03:52:32.065 RideOnTime[78:207] Sec 16
2010-07-17 03:52:32.071 RideOnTime[78:207] Sec 17
2010-07-17 03:52:32.077 RideOnTime[78:207] Sec 18
2010-07-17 03:52:32.083 RideOnTime[78:207] Sec 19
2010-07-17 03:52:32.089 RideOnTime[78:207] Sec 20
2010-07-17 03:52:32.094 RideOnTime[78:207] Sec 21
2010-07-17 03:52:32.099 RideOnTime[78:207] Sec 22
2010-07-17 03:52:32.156 RideOnTime[78:207] Sec 23
2010-07-17 03:52:32.167 RideOnTime[78:207] Sec 0

○取得方法その2
2010-07-17 03:52:38.298 RideOnTime[78:207] SecNm: 5
2010-07-17 03:52:38.305 RideOnTime[78:207] SecNm: 6
2010-07-17 03:52:38.310 RideOnTime[78:207] SecNm: 7
2010-07-17 03:52:38.316 RideOnTime[78:207] SecNm: 8
2010-07-17 03:52:38.321 RideOnTime[78:207] SecNm: 9
2010-07-17 03:52:38.326 RideOnTime[78:207] SecNm: 1
2010-07-17 03:52:38.331 RideOnTime[78:207] SecNm: 2
2010-07-17 03:52:38.338 RideOnTime[78:207] SecNm: 0


【iPhone3GS + iOS4】意図せざる並び...

○取得方法その1
2010-07-17 03:48:12.698 RideOnTime[497:307] Sec 0
2010-07-17 03:48:12.702 RideOnTime[497:307] Sec 5
2010-07-17 03:48:12.708 RideOnTime[497:307] Sec 6
2010-07-17 03:48:12.713 RideOnTime[497:307] Sec 7
2010-07-17 03:48:12.718 RideOnTime[497:307] Sec 8
2010-07-17 03:48:12.723 RideOnTime[497:307] Sec 9
2010-07-17 03:48:12.729 RideOnTime[497:307] Sec 10
2010-07-17 03:48:12.734 RideOnTime[497:307] Sec 11
2010-07-17 03:48:12.739 RideOnTime[497:307] Sec 12
2010-07-17 03:48:12.744 RideOnTime[497:307] Sec 13
2010-07-17 03:48:12.749 RideOnTime[497:307] Sec 14
2010-07-17 03:48:12.754 RideOnTime[497:307] Sec 15
2010-07-17 03:48:12.760 RideOnTime[497:307] Sec 16
2010-07-17 03:48:12.765 RideOnTime[497:307] Sec 17
2010-07-17 03:48:12.770 RideOnTime[497:307] Sec 18
2010-07-17 03:48:12.775 RideOnTime[497:307] Sec 19
2010-07-17 03:48:12.781 RideOnTime[497:307] Sec 20
2010-07-17 03:48:12.785 RideOnTime[497:307] Sec 21
2010-07-17 03:48:12.790 RideOnTime[497:307] Sec 22
2010-07-17 03:48:12.796 RideOnTime[497:307] Sec 23

○取得方法その2
2010-07-17 03:48:15.293 RideOnTime[497:307] SecNm: 0
2010-07-17 03:48:15.295 RideOnTime[497:307] SecNm: 5
2010-07-17 03:48:15.306 RideOnTime[497:307] SecNm: 6
2010-07-17 03:48:15.309 RideOnTime[497:307] SecNm: 7
2010-07-17 03:48:15.312 RideOnTime[497:307] SecNm: 8
2010-07-17 03:48:15.314 RideOnTime[497:307] SecNm: 9
2010-07-17 03:48:15.316 RideOnTime[497:307] SecNm: 1
2010-07-17 03:48:15.319 RideOnTime[497:307] SecNm: 2

--

 ご覧のようにiOS4だと0が始めに来てしまいます。おかげでセクションと時刻データとの組み合わせにもズレが生じて,TableViewが混乱してしまっています。

 デベロッパドキュメントにはinitWithFetchRequest:managedObjectContext:sectionNameKeyPath:cacheName:メソッドについて次のように書いてあります。

sectionNameKeyPath
A key path on result objects that returns the section name. Pass nil to indicate that the controller should generate a single section.

The section name is used to pre-compute the section information.

If this key path is not the same as that specified by the first sort descriptor in fetchRequest, they must generate the same relative orderings. For example, the first sort descriptor in fetchRequest might specify the key for a persistent property; sectionNameKeyPath might specify a key for a transient property derived from the persistent property.

 う〜む,iOS4ではこの記述通りになっていないような気がするのですが...。