Cocoaの日々: 画像を横に並べたスクロールビューアの作成 [3] 循環スクロール(無限スクロール?)
Cocoaの日々: 画像を横に並べたスクロールビューアの作成 [4] 実機確認(修正)
今回は自動スクロールを実装する。
自動スクロール
自動スクロールとはユーザの操作を待たずに勝手にスクロールすること。
02
ユーザが指でドラッグすると自動スクロールは一旦中止され、ドラッグが終わってからしばらくすると再開する。
実装
自動スクロールさせるには NSTimer を使って一定間隔で UIScrollView をスクロールさせればよい。UIScrollView の contentOffset プロパティを変更するとスクロールが発生するのでこれを NSTimer発火時に変更する。発火間隔は 0.05秒とした。この位が見た目ちょうどいい。
- (void)timerDidFire:(NSTimer*)timer { if (autoScrollStopped_) { return; } CGPoint p = self.scrollView.contentOffset; p.x = p.x + 1; if (p.x < IMAGE_WIDTH * MAX_SCROLLABLE_IMAGES) { self.scrollView.contentOffset = p; } }autoScrollStopped_ は BOOL型の変数でユーザが操作を行っている時には YES が入る。これによってタイマーが発火しても自動スクロールは発生しない。なお UIScrollView.contentSize は有限なので境界値チェックをいれている。これによって右端まで来た場合は自動スクロールは停止する。自動スクロールのメインとなる処理はこれだけ。 他には、ユーザの操作によって autoScrollStopped_ を YES/NO で切り替える処理がある。これは UISCrollViewDelegate のメソッドを使う。
まずユーザが指でドラッグを開始した場合の処理
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self stopAutoScroll]; } - (void)stopAutoScroll { autoScrollStopped_ = YES; }そしてドラッグが終わった時の処理。これは2つ必要。
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { [self restartAutoScrollAfterDelay]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (!decelerate) { [self restartAutoScrollAfterDelay]; } }scrollViewDidEndDecelerating だけでは不足で、scrollViewDidEndDragging:willDecelerate: も必要。
再開処理は一手間掛けてある。
- (void)restartAutoScroll { autoScrollStopped_ = NO; } - (void)restartAutoScrollAfterDelay { [self performSelector:@selector(restartAutoScroll) withObject:nil afterDelay:AUTO_SCROLL_DELAY]; }ユーザ操作完了後すぐに自動スクロールが発生するとせわしない?感じがするので若干の遅れ(AUTO_SCROLL_DELAY=3秒)を持たせている。
UISCrollView のドラッグとスクロールの終わり
UIScrollView を指でドラッグして離す(フリック?)と、しばらくの間慣性スクロールが続きやがて速度を落として止まる。この止まったタイミングで scrollViewDidEndDecelerating: が呼び出される。このタイミングで自動スクロールを再開させればいいのだが、実際に触ってみると自動スクロールが再開しないケースがあることがわかった。それはドラッグした指をフリックせずに(弾かずに)ピタっと止めた場合。この場合は scrollViewDidEndDecelerating が呼び出されない。このメソッドはその名の通り「速度を落とす」(Decelerate)動作が終了した時だけ呼ばれて、このようにピタっと止めた場合には呼ばれない。
このピタっと止めた場合のイベントを拾うのに同じ UIScrollViewDelegate の scrollViewDidEndDragging:willDecelerate: を使う。このメソッドはスクロールの終わりではなく、ユーザのドラッグ操作の終わりを検出する。2番目の引数 decelerate はドラッグ終了時に慣性スクロールが働いているかどうかを表している。decelerate == YES の場合は、このメソッドが呼び出された時もまだスクロールが終わっていない。指でピタっと止めたときに呼び出された時には decelerate == NO で呼び出される。 まとめると次の通り。
(a) フリックしてスクロール
ドラッグ ↓ scrollViewWillBeginDragging ↓ 離す(フリック) | |慣性スクロール ↓ scrollViewDidEndDragging:willDecelerate: (decelerate==YES) | |減速 | |スクロール停止 ↓ scrollViewDidEndDecelerating:(b) ドラッグしてピタっと止める
ドラッグ ↓ scrollViewWillBeginDragging ↓ ピタっと止める ↓ scrollViewDidEndDragging:willDecelerate: (decelerate==NO)上記動作により自動スクロール再開の為に2つのメソッドをチェックしている。(a)パターンではスクロール終了前に scrollViewDidEndDragging:willDecelerate:(YES) が呼び出されるので、その場合には自動スクロールの再開はさせないようにしている。このパターンの場合はその後に続く scrollViewDidEndDecelerating: で再開させている。
サンプル
見た目は前回と変わらないので割愛。
ソースは GitHub からどうぞ。
CirculationScroll at 2010-08-20 from xcatsan's iOS-Sample-Code - GitHub
Responses
Leave a Response