前回に引き続き、画像ビューワの作り方を解説していこう。今日は、画像を表示してフリックによるスクロールに対応するところまでだ。
ビューコントローラの宣言
この画像ビューワでは、画像の読み込みやフリックへの対応といった処理は、ビューコントローラで行っている。まずは、その定義を紹介しよう。次のような宣言にした。
List 1.
@interface ImageViewController : UIViewController
{
int _index;
IBOutlet UIScrollView* _mainScrollView;
IBOutlet UIView* _innerView;
IBOutlet UIScrollView* _subScrollView;
IBOutlet UIImageView* _imageView0;
IBOutlet UIImageView* _imageView1;
IBOutlet UIImageView* _imageView2;
}
画像ビューワの内部状態を管理するためのインスタンス変数が、1つだけある。_indexという名前の変数だ。この変数は、現在何枚目の画像を表示しているかを表すものだ。従って、中央に表示する画像は_indexにあたるもの。左の画像は_index-1、右の画像は_index+1ということになる。
あとは、Interface Builderで配置したビューに対するアウトレットだ。
また、Interface Builderでの設定も確認しておこう。まず、UIScrollViewのデリゲートとして、このビューコントローラを接続する。フリック操作の入力は、UIScrollViewのデリゲートメソッドとして受け取るからだ。
さらに、前回説明し忘れたちょっとしたテクニックを紹介しよう。この画像ビューワでは横に3枚画像を並べるのだが、それらがぴったりくっついて配置されていると、見づらい。そこで、それぞれの画像の間に余白を挟んで配置した。だが、何も考えずにこのまま動かすと、UIScrolViewによるページングの位置がずれてしまう。ページングでは、UIScrollViewの大きさにあわせてページの大きさを決めるからだ。では、どうすればいいか?こういうときは、UIScrollViewの大きさを、余白分広げてやればいい。今回の場合は余白を20ピクセルにしたので、メインスクロールビューの大きさを、画面より横に20ピクセル大きくしている。これにより、期待通りのページングが行われることになる。
フリック操作の受付
続いて、実装に入る。まず初期化として、画像3枚用のUIImageViewを持っているビューを、メインスクロールビューに設定する。このとき、スクロールビューのコンテントサイズの設定も行っておこう。さらに、インスタンス変数の初期化も行っておく。
List 2.
- (void)viewDidLoad
{
// innerViewをメインスクロールビューに追加
[_mainScrollView addSubview:_innerView];
// メインスクロールビューのコンテントサイズを設定
_mainScrollView.contentSize = _innerView.frame.size;
// インデックスの初期値として-1を設定
_index = -1;
}
次に、UIScrollViewDelegateメソッドを実装する。今回実装するのは、scrollViewDidEndDragging:willDecelerate:と、scrollViewDidEndDecelerating:の2つだ。これらが呼び出されたことによって、ユーザがフリック操作を行ったと判定しているのだ。
これらの中でやっていることは、ただ一つ。画像の更新を行う_renewImagesメソッドを呼び出すことだ。このメソッドについては、次で説明する。
List 3.
- (void)scrollViewDidEndDragging:(UIScrollView*)scrollView
willDecelerate:(BOOL)decelerate
{
// メインスクロールビューの場合
if (scrollView == _mainScrollView) {
if (!decelerate) {
// 画像の更新
[self _renewImages];
}
}
}
- (void)scrollViewDidEndDecelerating:(UIScrollView*)scrollView
{
// メインスクロールビューの場合
if (scrollView == _mainScrollView) {
// 画像の更新
[self _renewImages];
}
}
画像の更新
さて、画像ビューワの処理の中心となるのが、これから説明する_renewImagesメソッドだ。
このメソッドで行う処理は、大きく2つに分かれる。まず、ユーザのフリック操作によって、前の画像に移動したか、次の画像に移動したかを判定する。次に、画像が移動した場合は、3枚の画像の更新処理を行うのだ。
まずは画像移動の処理を行うソースコードを紹介しよう。
List 4.
- (void)_renewImages
{
CGRect rect;
NSString* fileName;
NSString* path;
UIImage* image;
// 現在のインデックスを保存
int oldIndex = _index;
// コンテントオフセットを取得
CGPoint offset;
offset = _mainScrollView.contentOffset;
if (offset.x == 0) {
// 前の画像へ移動
_index--;
}
if (offset.x >= _mainScrollView.contentSize.width - CGRectGetWidth(_mainScrollView.frame)) {
// 次の画像へ移動
_index++;
}
// インデックスの値をチェック
if (_index < 0) {
_index = 0;
}
if (_index > _maxPage - 1) {
_index = _maxPage - 1;
}
if (_index == oldIndex) {
return;
}
...
フリックで移動したかどうかは、メインスクロールビューのコンテントオフセットのx座標の値で判定する。xが0であれば、いちばん左までスクロールされたので、前の画像に移動する。xが右側の画像を表示している値であれば、次の画像に移動するのだ。このようにして、_indexの値を更新している。
画像が移動した場合は、3つの画像の更新処理を行う。長くなってしまうので、左側の画像の更新を行うところだけ紹介しよう。
List 5.
...
//
// 左側のimage viewを更新
//
// 最初の画像のとき
if (_index == 0) {
// 左側のimage viewは表示しない
rect = CGRectZero;
image = nil;
}
// 最初の画像以外のとき
else {
// 左側のimage viewのframe
rect.origin = CGPointZero;
rect.size = self.view.frame.size;
// 左側のimage viewの画像の読み込み
fileName = [NSString stringWithFormat:@"%03ds", _index - 1];
path = [[NSBundle mainBundle] pathForResource:fileName ofType:@"jpg"];
image = [[UIImage alloc] initWithContentsOfFile:path];
}
// 左側のimage viewの設定
_imageView0.frame = rect;
_imageView0.image = image;
[image release];
...
_indexの値をもとに、画像とそれを表示するためのフレームの大きさを決定している。_indexが0であれば、左には画像を表示しない。それ以外の場合は、_index-1の画像を表示するのだ。画像の読み込みは、今回は画像のファイル名を「001s.jpg」という感じにしたので、数値を当てはめてファイル名を決定し読み込んでいる。
このような処理を、真ん中と右の画像に対しても行ってやる。
最後に、メインスクロールビューの更新を行う。コンテントサイズとコンテントオフセットを更新してやるのだ。
List 6.
...
//
// メインスクロールビューの更新
//
// コンテントサイズとオフセットの設定
CGSize size;
size.width = CGRectGetMaxX(_imageView2.frame) > 0 ?
CGRectGetMaxX(_imageView2.frame) + 20.0f :
CGRectGetMaxX(_subScrollView.frame) + 20.0f;
size.height = 0;
_mainScrollView.contentSize = size;
_mainScrollView.contentOffset = _subScrollView.frame.origin;
}
コンテントサイズは、いちばん最初または最後の画像を表示しているときは画像2枚分、それ以外のときは画像3枚分となる。コンテントオフセットは、サブスクロールビューが表示されるところにあわせてやればいい。
これで、フリックによる画像ビューワは出来上がりだ。次回は、ピンチイン/アウトによる拡大について議論しよう。
ここまでのソースコード: Image-1.zip