プログラムにフリックさせてイベントを受け取る

  • フリックで左右にスクロールできるだけでなく、ナビゲーションバーにあるボタンからも左右にスクロールできるUIを想定。(標準のメールアプリみたいなイメージ)
  • ボタンからスクロールした場合も、フリックしたときと同様に、スクロール前後のイベントを受け取ってあれこれやりたい。
- (void)viewDidLoad {
	[super viewDidLoad];

	self.scrollContainer.animationDelegate = self;
}

- (void)scrollViewWillBeginDragging:(ScrollContainer *)scrollContainer {
	NSLog(@"offset: %d", self.scrollContainer.contentOffset.x);
}

- (void)scrollViewDidEndDecelerating:(ScrollContainer *)scrollContainer {
	NSLog(@"offset: %d", self.scrollContainer.contentOffset.x);
}

- (void)segmentDidChange:(id)sender {
	if (![sender isKindOfClass:[UISegmentControl class]]) {
		return;
	}
	UISegmentControl *segmentControl = sender;

	if (segmentControl.selectedSegmentIndex == 0) {
		[self.scrollContainer moveToPreviousContent];
	} else if (segmentControl.selectedSegmentIndex == 1) {
		[self.scrollContainer moveToNextContent];
	}
}
  • UISegmentControlで作ったボタンが押されると、ScrollContainerクラスのメソッドを呼び出して、左右どちらかにスクロールされる。
  • ScrollContainerは以下のようなUIScrollViewのサブクラス。
static CGFloat kContentWidth = 320.0f;

- (void)moveToNextContent {
	[UIView beginAnimation:nil context:NULL];
	[UIView setAnimationDuration:0.3f];
	[UIView setAnimationDelegate:self.animationDelegate];
	[UIView setAnimationWillStartSelector:@selector(scrollViewWillBeginDragging:)];
	[UIView setAnimationDidStopSelector:@selector(scrollViewDidEndDecelerating:)];

	CGPoint nextContentOffset = CGPointMake(self.contentOffset.x + kContentWidth, 0);
	self.contentOffset = nextContentOffset;

	[UIView commitAnimations];
}

- (void)moveToPreviousContent {
	[UIView beginAnimation:nil context:NULL];
	[UIView setAnimationDuration:0.3f];
	[UIView setAnimationDelegate:self.animationDelegate];
	[UIView setAnimationWillStartSelector:@selector(scrollViewWillBeginDragging:)];
	[UIView setAnimationDidStopSelector:@selector(scrollViewDidEndDecelerating:)];

	CGPoint previousContentOffset = CGPointMake(self.contentOffset.x - kContentWidth, 0);
	self.contentOffset = previousContentOffset;

	[UIView commitAnimations];
}
  • setContentOffset:animated:メソッドでもプログラムによるフリックを実現できるんだけど、スクロール前後のイベントを受け取ってあれこれすることはできないっぽい。
  • そこで、UIViewクラスのアニメーションを使って、アニメーション前後のイベントを利用する。
  • setAnimationWillStartSelector:setAnimationDidStopSelector:に直接scrollViewDidEndDecelerating:などを指定することで、プログラムによるフリックの場合でもイベントを受け取ることができる。