36

我试图设置持续时间(使用我会尝试的普通方法)来设置 UICollectionView 的selectItemAtIndexPath:animated:scrollPosition:方法(或scrollToItemAtIndexPath:atScrollPosition:animated:方法)的动画持续时间。我试过[UIView setAnimationDuration]了,我试过把它包装在一个CATransaction. 到目前为止,我在更​​改动画持续时间方面一直没有成功(尽管我承认我可能在这个逻辑上犯了一个错误)。

想法?

更新:

我在这里尝试了很多方法。最接近的解决方案是做我们通常对UIScrollView动画所做的事情(通过设置动画:参数NO并将其包装在UIView动画块中)。这对于滚动视图非常有效。UICollectionView但是,由于某种原因,这与创建过程有关。

我在下面包含了一个使用两种方法的示例。每种方法都假设您有 4 个部分,每个部分包含 4 个项目。此外,动画假定您从 0,0 移动到 3,3。

使用默认动画

这里的部分问题当然似乎与UICollectionView. 如果您采用以下方法(使用默认动画选项) - 一切正常:

[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:3 inSection:3]
                                atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                        animated:YES];

执行此操作时,将创建当前可见单元格和目标单元格之间的每个单元格。我已包括登录该collectionView:cellForItemAtIndexPath:方法:

2013-05-18 09:33:24.366 DEF-CV-Testing[75463:c07] Transition
Cell Created for Index Path: <NSIndexPath 0x8913f40> 2 indexes [0, 1]
Cell Created for Index Path: <NSIndexPath 0x75112e0> 2 indexes [0, 2]
Cell Created for Index Path: <NSIndexPath 0xfe1a6c0> 2 indexes [0, 3]
Cell Created for Index Path: <NSIndexPath 0x89159e0> 2 indexes [1, 0]
Cell Created for Index Path: <NSIndexPath 0x8a10e70> 2 indexes [1, 1]
Cell Created for Index Path: <NSIndexPath 0x7510d90> 2 indexes [1, 2]
Cell Created for Index Path: <NSIndexPath 0x75112a0> 2 indexes [1, 3]
Cell Created for Index Path: <NSIndexPath 0x8915a00> 2 indexes [2, 0]
Cell Created for Index Path: <NSIndexPath 0x75111c0> 2 indexes [2, 1]
Cell Created for Index Path: <NSIndexPath 0xfe17f30> 2 indexes [2, 2]
Cell Created for Index Path: <NSIndexPath 0xfe190c0> 2 indexes [2, 3]
Cell Created for Index Path: <NSIndexPath 0xfe16920> 2 indexes [3, 0]
Cell Created for Index Path: <NSIndexPath 0x75112a0> 2 indexes [3, 1]
Cell Created for Index Path: <NSIndexPath 0xfe1a4f0> 2 indexes [3, 2]
Cell Created for Index Path: <NSIndexPath 0x75142d0> 2 indexes [3, 3]

使用自定义动画

scrollToItemAtIndexPath:方法包装在UIView动画块中时,无法正确创建项目。在此处查看代码示例:

[UIView animateWithDuration:5.0 delay:0.0 options:0 animations:^{
    [self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:3 inSection:3]
                                atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                        animated:NO];
} completion:^(BOOL finished) {
    NSLog(@"Completed");
}];

当前可见的单元格消失,只创建目标单元格。我在该collectionView:cellForItemAtIndexPath:方法中包含了相同的日志记录:

Transition
Cell Created for Index Path: <NSIndexPath 0x71702f0> 2 indexes [3, 3]
Completed
4

7 回答 7

8

我有类似的问题UITableView,我通过以下代码解决了:

[CATransaction begin];
[CATransaction setCompletionBlock:onCompletion];
[CATransaction setAnimationDuration:duration];

[self.collectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:3 inSection:3]
                            atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                    animated:YES];

[CATransaction commit];

显然,你不能用它来调用它,animated:NO因为方法内的动画代码很重要。使用 aCATransaction来包装动画对我有用。

于 2013-05-22T13:56:09.833 回答
1

对我来说这有效。显然它需要一些调整(以获得更准确的时间并使其反向滚动,从下到上。现在它只支持从上到下的垂直滚动)

调用这样的东西:

[self scrollToIndexPath:[NSIndexPath indexPathForItem:40 inSection:0] withAnimationDuration:3.0f];

这是代码

-(void)scrollToIndexPath:(NSIndexPath *)path withAnimationDuration:(float)duration
{
    NSIndexPath *firstCell = [[self.collectionView indexPathsForVisibleItems] objectAtIndex:0];
    UICollectionViewLayoutAttributes *attributesForFirstCell = [self.collectionView layoutAttributesForItemAtIndexPath:firstCell];
    CGRect firstCellRect = attributesForFirstCell.frame;
    CGPoint startPoint = firstCellRect.origin;

    NSIndexPath *destinationCell = path;
    UICollectionViewLayoutAttributes *attributesForDestCell = [self.collectionView layoutAttributesForItemAtIndexPath:destinationCell];
    CGRect destCellRect = attributesForDestCell.frame;
    CGPoint destPoint = destCellRect.origin;

    self.destination = destPoint;

    float delta = destPoint.y - startPoint.y;

    float cycle = (delta / (50*duration));
    self.pass = (int)cycle + 1;

    self.myTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(scrollView) userInfo:nil repeats:YES];
}



- (void) scrollView {

    float w = self.pass;

    CGPoint scrollPoint = self.collectionView.contentOffset;
    scrollPoint.y = scrollPoint.y + w;

    if (scrollPoint.y >= self.destination.y)
        [self.myTimer invalidate];
    [self.collectionView setContentOffset: scrollPoint animated: NO];
}
于 2013-05-25T18:35:38.300 回答
1

我可以通过使用动画false将其包装在 UIView.animate 中来使其工作:

UIView.animate(withDuration: 0.7, delay: 0.0, options: .curveEaseInOut, animations: {
            self.collectionView.scrollToItem(at: IndexPath(row: 0, section: 0), at: .centeredHorizontally, animated: false)
        }, completion: nil)
于 2018-11-27T06:09:53.363 回答
0

如果你放一些代码scrollViewDidScroll:呢?

我有一个类似的问题,我通过调用解决了这个问题,selectItemAtIndexPath:animated:scrollPosition然后使用scrollViewDidScroll:它来运行我通常在完成块中运行的东西。

当然,您可能必须跟踪一BOOL两个来决定是否需要在scrollViewDidScroll:.

于 2013-07-23T11:46:27.757 回答
0

您可以使用AnimationEngine库干净地完成此操作。它包装了CADisplayLink基于块的动画语法。我在这里发布了一个带有动画的代码示例的答案contentOffset。您只需要做从 indexPath 到 contentOffset 的转换。

于 2015-06-25T22:37:46.693 回答
-1

我找到了一种手动滚动并完全绕过self.collectionView scrollToItemAtIndexPath..

我还尝试使用完成块来循环浏览不同的部分,但这导致了生涩的体验。

请注意,此示例垂直滚动——您应该能够将其更改为水平滚动。您可能还必须调整偏移量,以便滚动在所需位置停止。同样,我很惊讶 API 没有提供控制速度的方法。

-(void)scrollMethod {

    // Find the point where scrolling should stop.
    UICollectionViewLayoutAttributes *attr = [self.collectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:3 inSection:3]];

    CGRect cellRect = attr.frame;

    self.source = self.collectionView.frame.origin;
    self.destination = cellRect.origin;
    self.myTimer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(scrollOn) userInfo:nil repeats:YES];

}

-(void)scrollOn {
    self.collectionView.contentOffset = self.source;
    // If we have crossed where we need to be, quit the timer
    if ( self.source.y > self.destination.y)
        [self.myTimer invalidate];

    self.source = CGPointMake(self.source.x, self.source.y+1);
}
于 2013-05-20T17:33:57.913 回答
-3

实际的解决方案是您需要在主队列中执行此 scrollToItemAtIndexPath 。这样只有你才能得到它的实际工作。

示例代码:

 dispatch_async(dispatch_get_main_queue(), ^{

    [collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];

});

于 2016-02-10T05:51:57.737 回答