I'm working on some ticker-like functionality and am using a UICollectionView
. It was originally a scrollView, but we figure a collectionView will make it easier to add/remove cells.
I am animating the collectionView with the following:
- (void)beginAnimation {
[UIView animateWithDuration:((self.collectionView.collectionViewLayout.collectionViewContentSize.width - self.collectionView.contentOffset.x) / 75) delay:0 options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionRepeat | UIViewAnimationOptionBeginFromCurrentState) animations:^{
self.collectionView.contentOffset = CGPointMake(self.collectionView.collectionViewLayout.collectionViewContentSize.width, 0);
} completion:nil];
}
This works fine for the scroll view, and the animation is happening with the collection view. However, only the cells that are visible at the end of the animation are actually rendered. Adjusting the contentOffset is not causing cellForItemAtIndexPath
to be called. How can I get the cells to render when the contentOffset changes?
EDIT: For a bit more reference (not sure if it's much help):
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
TickerElementCell *cell = (TickerElementCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"TickerElementCell" forIndexPath:indexPath];
cell.ticker = [self.fetchedResultsController objectAtIndexPath:indexPath];
return cell;
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// ...
[self loadTicker];
}
- (void)loadTicker {
// ...
if (self.animating) {
[self updateAnimation];
}
else {
[self beginAnimation];
}
}
- (void)beginAnimation {
if (self.animating) {
[self endAnimation];
}
if ([self.tickerElements count] && !self.animating && !self.paused) {
self.animating = YES;
self.collectionView.contentOffset = CGPointMake(1, 0);
[UIView animateWithDuration:((self.collectionView.collectionViewLayout.collectionViewContentSize.width - self.collectionView.contentOffset.x) / 75) delay:0 options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionRepeat | UIViewAnimationOptionBeginFromCurrentState) animations:^{
self.collectionView.contentOffset = CGPointMake(self.collectionView.collectionViewLayout.collectionViewContentSize.width, 0);
} completion:nil];
}
}
Here is a swift implementation, with comments explaining why this is needed.
The idea is the same as in devdavid's answer, only the implementation approach is different.
You could try using a CADisplayLink to drive the animation yourself. This is not too hard to set up since you are using a Linear animation curve anyway. Here's a basic implementation that may work for you:
You should simply add
[self.view layoutIfNeeded];
inside the animation block, like so:If you need to start animation before user start dragging UICollectionView (e.g. from one page to another page), you can use this workaround to preload side cells:
Without this workaround, UICollectionView will remove cells, that not intersects target bounds, before the animation starts.
P.S. This working only if you need animate to next or previous page.
I suspect that
UICollectionView
is trying to improve performance by waiting until the end of the scroll before updating.Perhaps you could divide the animation up into chucks, although I'm not sure how smooth that would be.
Or maybe calling setNeedsDisplay periodically during the scroll?
Alternatively, perhaps this replacement for UICollectionView will either do want you need or else can be modified to do so:
https://github.com/steipete/PSTCollectionView
Use
:scrollToItemAtIndexPath
instead: