iOS基于content滚动一个UIScrollView另一个偏移

时间:2018-11-26 00:47:45

标签: ios iphone uiscrollview uicollectionview

编辑:我在底部附加了一个简单的演示项目Dropbox链接

我有一个UI,其顶部为UICollectionView,底部为scrollview。我希望collectionview中的滚动也同步滚动scrollview。我已在scrollview中禁用了用户交互功能,因此只有collectionview才能影响其中的滚动。

出于此测试目的,每个collectionview项目均为150px。

scrollview中的UIViews是屏幕宽度。因此,对于collectionview项目的每个滚动,我需要按屏幕宽度滚动scrollview。为此,我要计算collectionview偏移量已更改的距离,然后将其除以单元格宽度(150),再乘以scrollview的宽度。

我正在尝试实现以下用户界面:

开始:

enter image description here

将collectionview滚动到单元格1:

enter image description here

将collectionview滚动到单元格2:

enter image description here

前几次这一切都很好,但是当我来回滚动集合视图几次到更长的距离(比如说单元格10-> 0-> 10-> 0依此类推)时,scrollview消失了通过“微小”的距离同步。为了说明这一点,请注意滚动视图右侧的第二个UIView中的“黄色”颜色:

enter image description here

我也可以通过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

2 个答案:

答案 0 :(得分:0)

请注意,在viewDidLoad方法myScrollView中,框架实际上并未设置为真实设备。

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

答案 1 :(得分:0)

您应该做一些不同的事情,即计算偏移量而不使用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];
}