【问题标题】:Creating slow scrolling to indexPath in UICollectionView在 UICollectionView 中创建缓慢滚动到 indexPath
【发布时间】:2012-10-11 23:59:25
【问题描述】:

我正在做一个项目,我正在使用 UICollectionView 创建一个“图像代码”,我在其中宣传一系列徽标。 collectionView 高 1 项,长 12 项,一次显示 2 到 3 项(取决于可见徽标的大小)。

我想做一个缓慢的自动滚动动画,从第一项到最后一项,然后重复。

有没有人能够完成这项工作?我可以让滚动工作使用

[myCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:(myImages.count -1) inSection:0] atScrollPosition:UICollectionViewScrollPositionRight animated:YES];

但这太快了!

[UIView animateWithDuration:10 delay:2 options:(UIViewAnimationOptionAutoreverse + UIViewAnimationOptionRepeat) animations:^{
    [myCollection scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:(myImages.count -1) inSection:0] atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
} completion:nil];

这会产生所需的滚动速度,但在系列中只有最后几个单元格可见。我怀疑它们(甚至是起始可见单元格)正在立即出队。

有什么想法吗?

【问题讨论】:

  • 我尝试了您的代码,实际上是 scrollToItemAtIndexPath 将您带到您传递的特定 indexPath 即 [myImages.count -1] ,因此该 indexPath 处的图像可见

标签: ios scroll uiviewanimation nsindexpath uicollectionview


【解决方案1】:

你可以试试这个方法:

@property (nonatomic, assign) CGPoint scrollingPoint, endPoint;
@property (nonatomic, strong) NSTimer *scrollingTimer;
@synthesize scrollingPoint, endPoint;
@synthesize scrollingTimer;

- (void)scrollSlowly {
    // Set the point where the scrolling stops.
    self.endPoint = CGPointMake(0, 300);
    // Assuming that you are starting at {0, 0} and scrolling along the x-axis.
    self.scrollingPoint = CGPointMake(0, 0);
    // Change the timer interval for speed regulation. 
    self.scrollingTimer = [NSTimer scheduledTimerWithTimeInterval:0.015 target:self selector:@selector(scrollSlowlyToPoint) userInfo:nil repeats:YES];
}

- (void)scrollSlowlyToPoint {
    self.collectionView.contentOffset = self.scrollingPoint;
    // Here you have to respond to user interactions or else the scrolling will not stop until it reaches the endPoint.
    if (CGPointEqualToPoint(self.scrollingPoint, self.endPoint)) {
        [self.scrollingTimer invalidate];
    }
    // Going one pixel to the right.
    self.scrollingPoint = CGPointMake(self.scrollingPoint.x, self.scrollingPoint.y+1);
}

【讨论】:

  • 这会使 UI 线程阻塞,如果您有任何其他需要更改焦点的控件(如在 tvos 中),这不起作用。
  • 如何让它自动反转并在动画块中重复而不弄乱它?
【解决方案2】:

我使用 “1 像素偏移” 技巧。以编程方式滚动时,只需将结尾 contentOffset.x 设置为比应有的多/少 1 个像素。这应该是不明显的。并在完成块中将其设置为实际值。这样您就可以避免单元过早出列问题并获得平滑的滚动动画;)

这是滚动到右侧页面的示例(例如从第 1 页到第 2 页)。请注意,在animations: 块中,我实际上滚动了一个像素(pageWidth * nextPage - 1)。然后我在completion: 块中恢复正确的值。

CGFloat pageWidth = self.collectionView.frame.size.width;
int currentPage = self.collectionView.contentOffset.x / pageWidth;
int nextPage = currentPage + 1;

[UIView animateWithDuration:1
                      delay:0
                    options:UIViewAnimationOptionCurveEaseOut
                 animations:^{
                     [self.collectionView setContentOffset:CGPointMake(pageWidth * nextPage - 1, 0)];
                 } completion:^(BOOL finished) {
                     [self.collectionView setContentOffset:CGPointMake(pageWidth * nextPage, 0)];
                 }];

【讨论】:

  • 无缘无故投反对票没有任何好处,你知道吗?
  • pageWidth 和 nextPage 没有在答案的上下文中定义
  • 非常适合向前滚动,但是当向后滚动时,单元格在仍然可见时会出列。
  • @Superwayne 使用 currentPage - 1 前进,currentPage + 1 后退
【解决方案3】:

您还可以“缓慢”滚动到 UICollectionView 的末尾,而不必从 indexpath 到 indexpath。我为 TVOS 应用程序上的集合视图快速创建了这个:

func autoScroll () {
    let co = collectionView.contentOffset.x
    let no = co + 1

    UIView.animateWithDuration(0.001, delay: 0, options: .CurveEaseInOut, animations: { [weak self]() -> Void in
        self?.collectionView.contentOffset = CGPoint(x: no, y: 0)
        }) { [weak self](finished) -> Void in
            self?.autoScroll()
    }
}

我只是在viewDidLoad() 中调用autoScroll(),其余的会自行处理。滚动的速度由UIView 的动画时间决定。您可以(我没有尝试过)添加一个带有 0 秒的 NSTimer,这样您就可以在用户滚动时使其无效。

【讨论】:

  • 我也在尝试做类似的事情,但想创建一个无限循环(在最后一个之后转到第一个)。你是怎么做到的?
  • @puru020 我也有这个问题。如果您有一个具有相同大小项目的集合视图,那么它可能会更容易。基本上,您所做的是将前几个项目添加到 collectionview 项目的末尾,并将项目范围的最后一个添加到开头。当最后一个项目出现在屏幕上时,您可以将contentOffset 重新定位到UICollectionView 的开头,而不会引起用户注意;因此:无限滚动。这可能会有所帮助:stackoverflow.com/questions/15549233/…
  • @PaulPeelen您不需要在 Swift UIView 块中设置捕获列表,因为这是在稍后阶段执行的。它不会创建保留周期。
  • 使用此代码,您需要确保在视图消失时停止滚动(当您导航到另一个视图但此视图仍然存在时),因为在我们的应用程序中,它会被连续调用(例如每秒数千次)最大化我们应用中的 CPU 内核。
  • 这是一个无限循环,每秒调用 1000 次。请注意,屏幕每秒仅刷新 30 次。
【解决方案4】:

对于其他发现此问题的人,我更新了 Masa 的建议以在 Swift 上工作,并且我在最后引入了一些缓和,因此它的行为更像是原始的 scrollItemsToIndexPath 动画调用。我有数百个项目在我看来,所以稳定的步伐不是我的选择。

var scrollPoint: CGPoint?
var endPoint: CGPoint?
var scrollTimer: NSTimer?
var scrollingUp = false

func scrollToIndexPath(path: NSIndexPath) {
    let atts = self.collectionView!.layoutAttributesForItemAtIndexPath(path)
    self.endPoint = CGPointMake(0, atts!.frame.origin.y - self.collectionView!.contentInset.top)
    self.scrollPoint = self.collectionView!.contentOffset
    self.scrollingUp = self.collectionView!.contentOffset.y > self.endPoint!.y

    self.scrollTimer?.invalidate()
    self.scrollTimer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector: "scrollTimerTriggered:", userInfo: nil, repeats: true)
}

func scrollTimerTriggered(timer: NSTimer) {
    let dif = fabs(self.scrollPoint!.y - self.endPoint!.y) / 1000.0
    let modifier: CGFloat = self.scrollingUp ? -30 : 30

    self.scrollPoint = CGPointMake(self.scrollPoint!.x, self.scrollPoint!.y + (modifier * dif))
    self.collectionView?.contentOffset = self.scrollPoint!

    if self.scrollingUp && self.collectionView!.contentOffset.y <= self.endPoint!.y {
        self.collectionView!.contentOffset = self.endPoint!
        timer.invalidate()
    } else if !self.scrollingUp && self.collectionView!.contentOffset.y >= self.endPoint!.y {
        self.collectionView!.contentOffset = self.endPoint!
        timer.invalidate()
    }
}

调整差异和修饰符的值可调整大多数情况下的持续时间/轻松程度。

【讨论】:

    【解决方案5】:

    在 .h 文件中, 声明这个计时器,

    NSTimer *Timer;
    

    放置这个计时器代码,

      [CollectionView reloadData];
      Timer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(AutoScroll) userInfo:nil repeats:YES];
    

    那么,

    #pragma mark- Auto scroll image collectionview
    -(void)AutoScroll
    {
        if (i<[Array count])
        {
            NSIndexPath *IndexPath = [NSIndexPath indexPathForRow:i inSection:0];
            [self.ImageCollectionView scrollToItemAtIndexPath:IndexPath
                atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
            i++;
            if (i==[Array count])
            {
                i=0;
            }
        }
    }
    

    希望对你有用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-05
      • 1970-01-01
      • 1970-01-01
      • 2012-03-16
      相关资源
      最近更新 更多