Search code examples
iosobjective-cscrolluicollectionviewscreen-resolution

iOS - inaccurate CGPoint value in collectionView contentOffset


I have created this infinite collectionView loop with 13 icons, however it's not working properly on 320 x 568 resolution devices. The issue is with calculating appropriate image contentOffset when scrolling (it's an animation - it's not touchable) and it's not centered after a while. Those images are equally spaced between as I want to show 3 icons on screen at once. When 3 icons passes previousContentOffset is x = 319.5 and it should be x = 320.0 Any suggestions of workaround for this particular case? Perhaps iterate by single icon? Results I am getting after 1 icon slide:

  • previousContentOffset = (CGPoint)(x = 106.5, y = 0),

  • width = (double)106.66666666666667,

  • currentContentOffset = (CGPoint)(x = 213.16666666666669, y = 0)

// Infinite loop

- (void)setupDataForCollectionView {

NSArray *originalArray = self.arrayImages;

NSMutableArray *workingArray = [originalArray mutableCopy];

self.dataArray = [workingArray mutableCopy];
}

// Implementation of infinite loop

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {

// calculate width for every device

double contentOffsetWhenFullyScrolledRight = self.collectionViewHorizontal.contentSize.width - self.collectionViewHorizontal.frame.size.width;

if (scrollView.contentOffset.x == contentOffsetWhenFullyScrolledRight) {

    NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:1 inSection:0];

    [self.collectionViewHorizontal scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];

} else if (scrollView.contentOffset.x == 0) {

    NSIndexPath *newIndexPath = [NSIndexPath indexPathForItem:([self.dataArray count] -2) inSection:0];

    [self.collectionViewHorizontal scrollToItemAtIndexPath:newIndexPath atScrollPosition:UICollectionViewScrollPositionLeft animated:NO];
   }
 }

// TimeInterval for animation

- (void)timerAction:(NSTimer *)timer {

//previous icon content offset
CGPoint previousContentOffSet = self.collectionViewHorizontal.contentOffset;

//next icon width
double width = self.collectionViewHorizontal.frame.size.width / 3;
// NSUInteger index = (int)(floor(self.collectionViewHorizontal.contentOffset.x / width) + 1);

// animate icons scroll view
CGPoint currentContentOffset = CGPointMake(previousContentOffSet.x + width, previousContentOffSet.y);

NSIndexPath *indexPath = [self.collectionViewHorizontal indexPathForItemAtPoint:currentContentOffset];
if (indexPath.row > self.dataArray.count - 4) {
    // add elements and reload
    [self.dataArray addObjectsFromArray:[self.arrayImages copy]];
    [self.collectionViewHorizontal reloadData];
}
// sliding animation
[UIView animateWithDuration:.3 delay:.0 usingSpringWithDamping:0.5 initialSpringVelocity:.0 options:kNilOptions animations:^{
    self.collectionViewHorizontal.contentOffset = currentContentOffset;
} completion:^(BOOL finished) {
    [self scrollViewDidEndDecelerating:self.collectionViewHorizontal];
}];
 }

Solution

  • I have implemented a workaround a while back, so I thought I should share. I decided to iterate with indexes of current icon instead of contentOffset. I have created a NSInteger property currentIndex and added in timer method.

    - (void)timerAction:(NSTimer *)timer {
    
     self.currentIndex++;
    
     //next icon width
     double width = self.collectionViewHorizontal.frame.size.width / 3;
    
    // animate icons scroll view
    CGPoint currentContentOffset = CGPointMake(self.currentIndex * width, 0.0);
    
    NSIndexPath *indexPath = [self.collectionViewHorizontal indexPathForItemAtPoint:currentContentOffset];
    if (indexPath.row > (self.dataArray.count /2) {
        // add elements and reload
        [self.dataArray addObjectsFromArray:[self.arrayImages copy]];
        [self.collectionViewHorizontal reloadData];
    }
    // sliding animation
    [UIView animateWithDuration:.3 delay:.0 usingSpringWithDamping:0.5 initialSpringVelocity:.0 options:kNilOptions animations:^{
        self.collectionViewHorizontal.contentOffset = currentContentOffset;
    } completion:^(BOOL finished) {
        [self scrollViewDidEndDecelerating:self.collectionViewHorizontal];
    }];
     }