如果要用代码实现UIScrollView的滚动,我的第一反应就是写一行类似这样的代码:
[scrollView setContentOffset:CGPointMake(0, 100) animated:YES];
苹果提供了这样一个接口,表明它希望第三方程序员使用这个接口来完成UIScrollView的滚动操作。但是我一直在想,它和用UIView的property animation,来直接改变contentOffset有什么不同呢?我为什么不这样写呢:
[UIView animateWithDuration:0.25
animations:^{
[scrollView setContentOffset:CGPointMake(0, 100)];
}];
写代码实验了一下,发现这是两种完全不同的滚动方式。
(1) 使用animated参数,可以获得正确的UIScrollViewDelegate的回调;而使用UIView动画则不能。
在苹果的官方文档中,对setContentOffset:animated:
这一方法会引起的回调有大概如下的解释:
如果animated这一参数设置为NO,或者直接设置contentOffset这个property,delegate会收到一个
scrollViewDidScroll:
消息。如果animated这一参数设置为YES,则在整个动画过程中,delegate会收到一系列的scrollViewDidScroll:
消息,并且当动画完成时,还会收到一个scrollViewDidEndScrollingAnimation:
消息。
实验证明,使用setContentOffset:animated:
方法得到的回调行为和官方文档中描述的一致。而使用UIView动画,则只能收到一次scrollViewDidScroll:
回调,不能收到scrollViewDidEndScrollingAnimation:
回调。
(2) 使用animated参数,可以获取到动画过程中contentOffset的值。
如果在动画开始之前,contentOffset均为(0, 0),分别执行接下来的两段代码:
[UIView animateWithDuration:0.25
animations:^{
[scrollView setContentOffset:CGPointMake(0, 100)];
}];
NSLog(@"%f", scrollView.contentOffset.y);//输出:100.000000
[scrollView setContentOffset:CGPointMake(0, 100) animated:YES];
NSLog(@"%f", scrollView.contentOffset.y);//输出:0.000000
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(@"%f", scrollView.contentOffset.y);//输出:25.500000,每次输出不保证一致
});
可以看出,使用UIView动画后,无论在什么时候查询contentOffset的值,得到的都是动画的最终值。而使用animated参数,可以在动画过程中得到与当前状态较为一致的contentOffset值。
参考这篇文章,改变UIScrollView的contentOffset,实际上应该就是在改变UIScrollView的bounds。按照苹果官方文档中的描述,bounds这个property是可以用作UIView动画的property。
但是setContentOffset:animated:
这一方法中实现的肯定不是UIView一个property animation那么简单。
实际工作中,我也发现,在tableView中使用animated参数这种方法呈现的动画效果的流畅程度明显低于UIView动画。但是考虑到UIScrollView的回调和苹果的推荐,还是应该尽量使用setContentOffset:animated:
这个接口来完成UIScrollView的滚动。