【问题标题】:iOS Scrolling one UIScrollView based off contentOffset of another goes out of synciOS基于另一个的contentOffset滚动一个UIScrollView不同步
【发布时间】: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


【解决方案1】:

注意viewDidLoad 方法中的myScrollView 框架并未真正设置为真实设备。

所以在视图的初始化中,视图的宽度和高度可能在您的代码中是错误的。

【讨论】:

  • 您对 viewDidLoad 中的宽度不正确是正确的,我将其修复为使用 viewDidLayoutSubviews。但是,这并不能解决偏移量不同步的实际问题,该问题仍然存在。
  • 删除 scrollViewWillBeginDragging 方法并初始化 previousOffset_HeaderpreviousOffset_Scrollview,在 videoDidLoad 中使用 0。系统调用scrollViewWillBeginDragging 似乎有延迟。
【解决方案2】:

您应该做一些不同的事情,计算偏移量的方式,而不是使用UIScrollViewDelegate 回调scrollViewDidEndDragging:willDecelerate:scrollViewDidEndDecelerating:。我已经重写了相关代码:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if (scrollView==self.myCollectionView) {
      [self calculateScrollviewOffset:self.myCollectionView];
    }
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
  if (scrollView == self.myCollectionView && !decelerate) {
    [self calculateEndPosition: self.myCollectionView];
  }
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
  if (scrollView == self.myCollectionView) {
    [self calculateEndPosition: self.myCollectionView];
  }
}

- (void) calculateScrollviewOffset:(UICollectionView *) collectionView {
  CGFloat cellWidth = 150.0;
  CGFloat percentageMoved = collectionView.contentOffset.x / cellWidth;
  [self setScrollViewOffset:percentageMoved animated:false];
}

- (void) calculateEndPosition:(UICollectionView*) collectionView {
  CGFloat cellWidth = 150.0;
  // NOTE: Add 0.5 to play around with the end animation positioning
  CGFloat percentageMoved = floor(collectionView.contentOffset.x / cellWidth);
  CGFloat collectionViewFixedOffset = (percentageMoved * cellWidth);
  [collectionView setContentOffset:CGPointMake(collectionViewFixedOffset, collectionView.contentOffset.y) animated:true];
  [self setScrollViewOffset:percentageMoved animated:true];
}

- (void) setScrollViewOffset:(CGFloat) percentageMoved animated:(BOOL) animated {
  CGFloat newOffsetX = percentageMoved * self.myScrollView.frame.size.width;
  [self.myScrollView setContentOffset:CGPointMake(newOffsetX, self.myScrollView.contentOffset.y) animated: animated];
}

【讨论】:

    猜你喜欢
    • 2012-05-06
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多