title: How to fix scrolling to a visible cell in UICollectionView with custom layout?
body: <p>I'm working on custom value picker inspired by UIPickerView. It looks like that:
  <img src="https://i.stack.imgur.com/IMmFv.png" alt="Technical prototype screen 1"></p>
  
  <p>As you can see, one of the main features of this picker is the central cell the should be wider than others to make it's neighbours visible beside of the central frame. When you scroll the picker with a pan gesture it should dynamically change central value and adjust cells according to the logic above. And it works just perfect:
  <img src="https://i.stack.imgur.com/dOc3i.png" alt="Technical prototype screen 2"></p>
  
  <p>The problem is in the tap gesture. When the user select any item on the picker by performing tap on it the picker trying to scroll to that item. But since it's offset was changed by the custom layout UIScrollView scrolls to the wrong point. And it looks like that:
  <img src="https://i.stack.imgur.com/PHegA.png" alt="Technical prototype screen 3"></p>
  
  <p>When I trying to scroll to the offscreen cell all things works fine - that cell was not affected by the layout and it's coordinates are correct. The issue rises only for visible cells. I'm completely out of any ideas of how to fix that. You could find the whole project here: <a href="https://github.com/plushcube/CarouselCollectionView" rel="nofollow noreferrer">Carousel Collection View Test Project</a></p>
  
  <p>Please find some significant code below.</p>
  
  <pre><code>// CarouselLayout.m
  
  - (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
      NSArray *array = [super layoutAttributesForElementsInRect:rect];
      CGRect visibleRect;
      visibleRect.origin = self.collectionView.contentOffset;
      visibleRect.size = self.collectionView.bounds.size;
  
      CGRect center = CGRectMake(CGRectGetMidX(visibleRect) - 1.0, 0.0, 2.0, CGRectGetHeight(visibleRect));
      CGFloat coreWidth = CGRectGetWidth(self.centralFrame) / 3.0;
  
      for (UICollectionViewLayoutAttributes *attributes in array) {
          if (CGRectIntersectsRect(attributes.frame, rect)){
              CGFloat distance = CGRectGetMidX(visibleRect) - attributes.center.x;
              CGFloat offset = 0.0;
              CGRect coreFrame = CGRectMake(attributes.center.x - (coreWidth / 2.0), 0.0, coreWidth, CGRectGetHeight(self.centralFrame));
              if (CGRectIntersectsRect(center, coreFrame)) {
                  if (attributes.indexPath.item % 2 == 0) {
                      self.centralItemOffset = (CGRectGetWidth(self.centralFrame) - CGRectGetWidth(attributes.frame) - 4.0) / 2.0;
                      if ([self.collectionView.delegate respondsToSelector:@selector(collectionView:layout:didChangeCentralItem:)]) {
                          [(id &lt;CarouselLayoutDelegate&gt;)self.collectionView.delegate collectionView:self.collectionView layout:self didChangeCentralItem:attributes.indexPath];
                      }
                  }
              }
              offset = (distance &gt; 0) ? -self.centralItemOffset : self.centralItemOffset;
              attributes.center = CGPointMake(attributes.center.x + offset, attributes.center.y);
          }
      }
      return array;
  }
  
  - (CGPoint)targetContentOffsetForProposedContentOffset:(CGPoint)proposedContentOffset withScrollingVelocity:(CGPoint)velocity {
      CGFloat offsetAdjustment = MAXFLOAT;
      CGFloat horizontalCenter = proposedContentOffset.x + (CGRectGetWidth(self.collectionView.bounds) / 2.0);
      CGRect targetRectHorizontal = CGRectMake(proposedContentOffset.x, 0.0, self.collectionView.bounds.size.width, self.collectionView.bounds.size.height);
  
      NSArray *array = [super layoutAttributesForElementsInRect:targetRectHorizontal];
      for (UICollectionViewLayoutAttributes *attributes in array) {
          if (attributes.indexPath.item % 2 == 1) {
              continue;
          }
          CGFloat itemHorizontalCenter = attributes.center.x;
          if (ABS(itemHorizontalCenter - horizontalCenter) &lt; ABS(offsetAdjustment)) {
              offsetAdjustment = itemHorizontalCenter - horizontalCenter;
          }
      }
  
      return CGPointMake(proposedContentOffset.x + offsetAdjustment, proposedContentOffset.y);
  }
  
  // ViewController.m
  
  - (void)collectionView:(UICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
      if (indexPath.item % 2 == 1) {
          return;
      }
      NSString *nextValue = [self valueAtIndexPath:indexPath];
      [self scrollToValue:nextValue animated:YES];
      self.currentValue = nextValue;
  }
  
  - (void)scrollToValue:(NSString *)value animated:(BOOL)animated {
      NSIndexPath *targetPath = nil;
      NSIndexPath *currentPath = nil;
      for (NSString *item in self.itemsArray) {
          if (!targetPath &amp;&amp; [value isEqualToString:item]) {
              targetPath = [NSIndexPath indexPathForItem:([self.itemsArray indexOfObject:item] * 2) inSection:0];
          }
          if (!currentPath &amp;&amp; [self.currentValue isEqualToString:item]) {
              currentPath = [NSIndexPath indexPathForItem:([self.itemsArray indexOfObject:item] * 2) inSection:0];
          }
          if (targetPath &amp;&amp; currentPath) {
              break;
          }
      }
      if (targetPath &amp;&amp; currentPath) {
          [self.itemsCollectionView scrollToItemAtIndexPath:targetPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:animated];
      }
  }
  </code></pre>
  
  <p>If it's not enough please ask for additional code in comments.</p>