【发布时间】:2019-04-27 15:33:52
【问题描述】:
编辑:我在底部附加了一个简单的演示项目保管箱链接
我有一个 UICollectionView 在顶部和滚动视图在底部。我希望collectionview中的滚动也可以同步滚动scrollview。我在滚动视图中禁用了用户交互,因此只有集合视图可以影响其中的滚动。
出于此测试目的,每个 collectionview 项目都是 150 像素。
滚动视图中的 UIView 的大小是屏幕宽度。因此,对于collectionview 项目的每个滚动,我都需要按屏幕宽度滚动滚动视图。为了实现这一点,我计算了collectionview偏移改变的距离,然后将它除以单元格宽度(150)并乘以滚动视图的宽度。
我正在尝试实现以下 UI:
开始:
将collectionview滚动到单元格1:
将collectionview滚动到单元格2:
这一切在前几次都很好,但是当我来回滚动collectionview几次到更长的距离(比如说单元格10 -> 0 -> 10 -> 0等等)时,滚动视图就会消失通过“微小”的距离同步。为了说明这一点,请注意滚动视图右侧边缘的第二个 UIView 是如何出现“黄色”的:
我也可以通过 NSLogging 滚动视图的 contentOffset 看到这个问题(注意几次后它开始不同步 0.5):
2018-11-25 19:24:28.273278-0500 ScrollViewMatchTest[19412:1203912] Finished: 0
2018-11-25 19:24:31.606521-0500 ScrollViewMatchTest[19412:1203912] Finished: 0.5
2018-11-25 19:24:55.173709-0500 ScrollViewMatchTest[19412:1203912] Finished: 1.5
2018-11-25 19:25:03.007528-0500 ScrollViewMatchTest[19412:1203912] Finished: 1.5
2018-11-25 19:25:07.841096-0500 ScrollViewMatchTest[19412:1203912] Finished: 2.5
2018-11-25 19:26:57.634429-0500
我不确定是什么导致了这个问题,我已经尝试了很多方法来解决它,但都是徒劳的。我可以想出一个解决方法(重置偏移量并在滚动完成时使其恢复同步),但我想知道为什么会导致这种不同步问题。
通过将scrollView的contentOffset重置为关闭屏幕宽度的倍数的解决方法:
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
if (scrollView==self.myCollectionView) {
NSLog(@"Finished: %g",self.myScrollView.contentOffset.x);
NSLog(@"Closest: %g",RoundTo(self.myScrollView.contentOffset.x, self.myScrollView.frame.size.width));
[self.myScrollView setContentOffset:CGPointMake(RoundTo(self.myScrollView.contentOffset.x, self.myScrollView.frame.size.width), self.myScrollView.contentOffset.y) animated:YES];
}
}
float RoundTo(float number, float to)
{
if (number >= 0) {
return to * floorf(number / to + 0.5f);
}
else {
return to * ceilf(number / to - 0.5f);
}
}
解决方案结束
我还附上了一个简单的演示项目来说明这个问题(运行应用程序并在顶部滚动视图上来回滚动几次):https://www.dropbox.com/s/e2bzgo6abq5wmgw/ScrollViewMatchTest.zip?dl=0
代码如下:
#import "ViewController.h"
#define countOfItems 50
@interface ViewController (){
CGFloat previousOffset_Header;
CGFloat previousOffset_Scrollview;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self.myCollectionView registerNib:[UINib nibWithNibName:@"MyCell" bundle:nil] forCellWithReuseIdentifier:@"cell"];
[self.myCollectionView reloadData];
for (NSInteger i=0; i<countOfItems; i++) {
UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(i*self.myScrollView.frame.size.width, 0, self.myScrollView.frame.size.width, self.myScrollView.frame.size.height)];
myView.backgroundColor=i%2==0?[UIColor blueColor]:[UIColor yellowColor];
[self.myScrollView addSubview:myView];
}
self.myScrollView.contentSize=CGSizeMake(countOfItems*self.myScrollView.frame.size.width, self.myScrollView.frame.size.height);
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return countOfItems;
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
MyCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell" forIndexPath:indexPath];
cell.backgroundColor = indexPath.item%2==0?[UIColor blueColor]:[UIColor yellowColor];
cell.myLabel.text=[NSString stringWithFormat:@"%ld",indexPath.item];
return cell;
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
if (scrollView==self.myCollectionView) {
previousOffset_Header = self.myCollectionView.contentOffset.x;
previousOffset_Scrollview = self.myScrollView.contentOffset.x;
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
if (scrollView==self.myCollectionView) {
[NSObject cancelPreviousPerformRequestsWithTarget:self];
[self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:scrollView afterDelay:0.1 inModes:@[NSRunLoopCommonModes]];
CGFloat offsetToMoveBy = (self.myCollectionView.contentOffset.x-previousOffset_Header)*(self.myScrollView.frame.size.width/150);
previousOffset_Scrollview = previousOffset_Scrollview +offsetToMoveBy;
[self.myScrollView setContentOffset:CGPointMake(previousOffset_Scrollview, self.myScrollView.contentOffset.y) animated:NO];
previousOffset_Header = self.myCollectionView.contentOffset.x;
}
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[NSObject cancelPreviousPerformRequestsWithTarget:self];
NSLog(@"Finished: %g",self.myScrollView.contentOffset.x);
}
@end
【问题讨论】:
-
那么问题解决了吗?如果是这样,请回答您自己的问题并接受您自己的答案。
-
@matt 它没有解决,因为我的解决方法有效,但 1. 我仍然想知道实际问题是什么,以及 2. 当您看到 scrollView 停止然后滚动时,解决方法看起来不太好再次修复自己。所以看起来有点丑。
标签: ios iphone uiscrollview uicollectionview