画像を横に並べたスクロールビューアの作成 [5] 自動スクロール

2010年8月20日金曜日 | Published in | 0 コメント

このエントリーをはてなブックマークに追加

[前回まで]
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

人気の投稿(過去 30日間)