iPhoneらしい画像ビューワ
最初のiPhoneが登場したときに標準で含まれていたアプリの1つに、「写真」アプリがある。標準的な機能を持つ画像ビューワなのだが、フリックによる画像間の移動や、ピンチイン/アウトによる拡大縮小など、iPhoneが提案する新しいユーザインタフェースの案内人として、大きなインパクトを与えた。
となれば、これと同じものを作ってみたいと考えるのがプログラマの心情だろう。早速作ってみたことのある読者の方もおられるかもしれないが、実はこれが意外に難しいのだ。確かにフリックやピンチイン/アウトといった操作は、スクロールビューを通じて提供される。だが、連続的な画像の読み込み、適切な拡大縮小の処理、デバイスの回転への対応など、解決しなくてはいけない問題も地味にたくさんある。
今回からは、画像ビューワを作ってみよう。機能としては標準的なものにするが、気をつけなくてはいけない点は結構多い。ちなみに、Appleが標準で提供するアプリは、ぱっと見ると機能や外見は控えめに思えるかもしれない。だが、そのクローンアプリを作ろうとすると、意外なところまで考え抜かれていることに気づかされる。この辺りは、長年コンシューマ向けソフトウェアを作り続けていた蓄積によるものだろう。
ビュー構成の設計
では、作ってみることにしよう。まず、アプリの機能とそれを実現するためのビュー構成を考える。
機能としてまず挙げられるのは、当然のことながら画像を表示することだ。これには、UIImageViewクラスを使おう。次に、複数の画像をフリックで切り替えられるようにしたい。そのために、UIScrollViewを使う。UIScrollViewの中に複数のUIImageViewを配置して、これをスクロールさせよう。さらに、現在表示している画像を拡大縮小したい。これにも、UIScrollViewを使おう。
つまり、UIScrollViewは2つ使うことになる。フリック用のものと、ピンチイン/アウト用のものだ。ピンチイン/アウトのものは常に画面の中央で行うので、このためのスクロールビューをメインスクロールビューと呼ぼう。フリックのためのものは、サブスクロールビューとする。
サブスクロールビューに配置する画像について、もう少し考えよう。単純に考えれば、表示する画像をずらっと横に並べてサブクロールビューの中に入れてしまえば、それでおしまいとなる。だが、このやり方は非常にまずい。なぜなら大量の画像を読み込むとあっという間にメモリを食い尽くしてしまい、アプリがクラッシュするからだ。読み込む画像の数は、できるだけ抑えたい。
では最小の数は何か? まず、現在表示している画像は絶対に必要だ。さらに、フリックで移動するため、左右の画像は読み込んでおきたい。これで事足りるはずだ。つまり、最低3つの画像を読み込んでおけばいい。フリックして移動したら、その瞬間に3枚の画像を更新するのだ。
これでビュー構成は固まった。下から順にいくと、まずフリック用のサブスクロールビューがある。これのスクロールサイズは、画面3つ分とする。その左右両端に、画像表示のためにUIImageViewを置く。真ん中には、まずピンチイン/アウト用のメインスクロールビュー。その中に、拡大画像表示用のUIImageView。このような構成にしよう。
Interface Builderによるレイアウト
機能を決めて設計を行ったので、実際の作成に移ろう。まずは、ユーザインタフェースの配置からだ。Interface Builderを使う。こういった複雑なビューは、やはりInterface Builderを使って視覚的に行うのがいちばん分かりやすい。
Xcodeを起動し、ビューコントローラベースのテンプレートを使ってプロジェクトを作る。そして、ビューコントローらが管理する.xibファイルを開こう。
ビュー階層の下から順に作っていく。まず、サブスクロールビューだ。ビューコントローラが管理するビューの上に、UIScrollViewを配置する。ビューのサイズは、画面の大きさにする。インスペクタを使って設定も行っておこう。まず、スクロールバーは表示したくないので、「Horizontal」と「Vertical」のチェックボックスをオフにする。さらに、1ページごとのスクロールにしたいので、「Paging Enabled」をオンにする。
次は、このサブスクロールビューがスクロールするためのビューだ。新たなUIViewを追加しよう。このビューのサイズは、画面3枚分になるのだが、画像を配置するときにそれぞれの間に余白が欲しい。そこで、余白を足した分の大きさにしよう。その中に、UIImageViewを配置する。左右両端はそのままで。真ん中のものは、まずメインスクロールビューとなるUIScrollViewを配置し、その中にUIImageViewを入れよう。
これでレイアウトはできあがった。次回からはコーディングに移ろう。