61

How can i find indexPath for cell in the middle of UICollectionView?

I have horizontal scrolling and only one big cell is visible (partially two other cells on the sides are visible as well). I need to delete cell located in the center (means - current cell) without touching it.

Only pressing "Trash" button and confirm Delete. But now it delete only first cells.

- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
    if (buttonIndex == actionSheet.destructiveButtonIndex) { 
        initialPinchPoint = ????????

        self.tappedCellPath = [self.collectionView indexPathForItemAtPoint:initialPinchPoint];

        Technique *technique = [arrayOfTechnique objectAtIndex:self.tappedCellPath.row];

        [self deleteData:[NSString stringWithFormat:@"DELETE FROM TECHNIQUES WHERE TECHNIQUENAME IS '%s'",[technique.techniquename UTF8String]]];

        [arrayOfTechnique removeObjectAtIndex:self.tappedCellPath.row];

        //[arrayOfTechnique removeObjectAtIndex:[custom_layout ]];
        [self removeImage:technique.techniquename];

        [self.collectionView performBatchUpdates:^{
            [self.collectionView deleteItemsAtIndexPaths:[NSArrayarrayWithObject:self.tappedCellPath]];
         } completion:nil];

         [self checkArrayCount];
    }
}
Aleksander Azizi
  • 9,829
  • 9
  • 59
  • 87
Sasha Prent
  • 1,151
  • 2
  • 10
  • 11

11 Answers11

41

Like you did yourself, indexPathForItemAtPoint is a good way of finding the index path of the element you're interested in. If your question is: how do i know the coordinates of this point? Then you should try with (using the same name you gave it in your code snippet):

initialPinchPoint = CGPointMake(self.collectionView.center.x + self.collectionView.contentOffset.x, 
                                self.collectionView.center.y + self.collectionView.contentOffset.y);
TheEye
  • 9,280
  • 2
  • 42
  • 58
micantox
  • 5,446
  • 2
  • 23
  • 27
  • 3
    collectionView.offset should be collectionView.contentOffset – dmur Jul 03 '14 at 00:13
  • Calculation with contentOffset is convoluted and not necessary if you just convert points from viewcontroller's view center. See alternative answer below. – Jlam Nov 30 '14 at 04:57
41

Here's what I did in Swift 3

private func findCenterIndex() {
    let center = self.view.convert(self.collectionView.center, to: self.collectionView)
    let index = collectionView!.indexPathForItem(at: center)
    print(index ?? "index not found")
}
MCR
  • 1,633
  • 3
  • 21
  • 36
34

This would be better code because it's cleaner and easier to read, all the content offset calculation is superfluous:

     NSIndexPath *centerCellIndexPath = 
[self.collectionView indexPathForItemAtPoint:
[self.view convertPoint:[self.view center] toView:self.collectionView]];

This would also be the correct representation of what you're actually trying to do:
1. Taking the center point of your viewcontroller's view - aka visual center point
2. convert it to the coordinate space of the view you're interested in - which is the collection view
3. Get the indexpath that exist at the location given.

Jlam
  • 1,932
  • 1
  • 21
  • 26
  • 2
    Since the original question asked for the cell in the *middle of the collection view*, this would be more correct: `CGPoint centerOfScrollableContent = [self.view convertPoint:self.collectionView.center toView:self.collectionView];` Important if the collectionview isn't the full size of `self.view`. – Jason Moore Mar 03 '16 at 18:38
  • No @JasonMoore if you read it carefully the author wanted the visual center - implying the center of the view one is looking at. The original solution is such that regardless of the frame of the collectionView, the visual center is converted properly. – Jlam Mar 03 '16 at 23:13
  • That's what I'm showing @Jlam – the visual center of the collection view, but converted to include the content offset. The `centerOfScrollableContent` is required in order to determine the collection view cell in the center: `[self.collectionView indexPathForItemAtPoint:centerOfScrollableContent]` – Jason Moore Mar 04 '16 at 02:12
  • **me** " implying the center of the view one is looking at", **you** "center of the collection view". Our assumptions are different - if the collection view origin is off to the right by 20px, your solution assumes the desired new center is now off to the right 20px, my solution assumes the desired new center remains the same. – Jlam Mar 04 '16 at 08:31
26

Swift:

extension UICollectionView {

var centerPoint : CGPoint {

    get {
        return CGPoint(x: self.center.x + self.contentOffset.x, y: self.center.y + self.contentOffset.y);
    }
}

var centerCellIndexPath: IndexPath? {

    if let centerIndexPath: IndexPath  = self.indexPathForItemAtPoint(self.centerPoint) {
        return centerIndexPath
    }
    return nil
}
}

Usage :

if let centerCellIndexPath: IndexPath  = collectionView.centerCellIndexPath {
                print(centerCellIndexPath)
            }

Swift 3:

extension UICollectionView {

var centerPoint : CGPoint {

    get {
        return CGPoint(x: self.center.x + self.contentOffset.x, y: self.center.y + self.contentOffset.y);
    }
}

var centerCellIndexPath: IndexPath? {

    if let centerIndexPath = self.indexPathForItem(at: self.centerPoint) {
        return centerIndexPath
    }
    return nil
}
}
Bartando
  • 719
  • 8
  • 26
Bobj-C
  • 5,276
  • 9
  • 47
  • 83
8

Thank you micantox!

I had multiple visible cells of UICollectionView and needed to position the cell at the centre of the collection view. Something similar to Adobe Content Viewer. If someone is struggling with similar scenario:

#pragma mark - UIScrollViewDelegate methods

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{
    CGPoint centerPoint = CGPointMake(self.pageContentCollectionView.center.x + self.pageContentCollectionView.contentOffset.x,
                                    self.pageContentCollectionView.center.y + self.pageContentCollectionView.contentOffset.y);
    NSIndexPath *centerCellIndexPath = [self.pageContentCollectionView indexPathForItemAtPoint:centerPoint];
    [self.pageContentCollectionView scrollToItemAtIndexPath:centerCellIndexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

}
DarkoM
  • 335
  • 4
  • 5
6

I made like for horizontal UICollectionView I use Swift 2.x.

private func findCenterIndex() {
    let collectionOrigin = collectionView!.bounds.origin
    let collectionWidth = collectionView!.bounds.width
    var centerPoint: CGPoint!
    var newX: CGFloat!
    if collectionOrigin.x > 0 {
        newX = collectionOrigin.x + collectionWidth / 2
        centerPoint = CGPoint(x: newX, y: collectionOrigin.y)
    } else {
        newX = collectionWidth / 2
        centerPoint = CGPoint(x: newX, y: collectionOrigin.y)
    }

    let index = collectionView!.indexPathForItemAtPoint(centerPoint)
    print(index)
}

 override func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
    findCenterIndex()
}
Alexander Khitev
  • 6,417
  • 13
  • 59
  • 115
2

UICollectionView has a method - (NSIndexPath *)indexPathForItemAtPoint:(CGPoint)point.
This method return the index path of the item at the specified point. You could calculate the point that represents the center of the UICollectionView and then use that CGPoint and this method to retrieve the index path of the item you want to delete.

s1m0n
  • 7,825
  • 1
  • 32
  • 45
  • Yes, i tried this indexPathForItemAtPoint but it delete first items as well. i'll try it again , and let u know! thanks – Sasha Prent Oct 09 '13 at 07:48
  • 1
    The first item would've got deleted because if the point is outside of the UICollectionViewCell but inside the UICollectionView, this method would return indexPath as nil and when you try to do something like "indexPath.row" it'll return 0 i.e. the first element. So best practice is to always put a nil check for indexPath in such cases. Hope this helps... – cirronimbo Nov 04 '14 at 09:22
1

For some of you that might be experiencing some troubles on getting the center, please look at this potential solution:

 func centerCell()->UICollectionViewCell? {
      // Asuming your scrolling is horizontal
      let viewHorizontalCenter =  self.view.bounds.width / 2
      let center = CGPoint(x: viewHorizontalCenter, y: self.collectionView.center.y)

      let convertedPoint = self.view.convert(center, to: self.unitsCollectionView)

      let center = CGPoint(x: self.view.bounds.width / 2, y: self.unitsCollectionView.center.y)
      let convertedPoint = self.view.convert(center, to: self.unitsCollectionView)
      for cell in unitsCollectionView.visibleCells {
          if cell.frame.contains(convertedPoint) {
              print("Hello")
              return cell
          }
      }
      return nil
    }
ugur
  • 3,604
  • 3
  • 26
  • 57
ndr.hax
  • 345
  • 3
  • 13
0

Try this protocol...

protocol CollectionVisibleMidCell {}
extension CollectionVisibleMidCell where Self: UICollectionView {

    func getMidVisibleIndexPath() -> IndexPath? {
        var visibleRect = CGRect()
        visibleRect.origin = self.contentOffset
        visibleRect.size = self.bounds.size
        let visiblePoint = CGPoint(x: visibleRect.midX, y: visibleRect.midY)
        guard let indexPath = self.indexPathForItem(at: visiblePoint) else { return nil }
        return indexPath
    }
}

extension UICollectionView: CollectionVisibleMidCell {}
James Rochabrun
  • 4,137
  • 2
  • 20
  • 17
0

Swift 4

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let indexPath = collectionView.indexPathForItem(at: collectionView.bounds.center)        
}
Fidel López
  • 1,022
  • 7
  • 7
0

Based on @micantox answer. Updated code for Swift 5

let initialPinchPoint = CGPoint(x: collectionView.center.x + collectionView.contentOffset.x,
                                y: collectionView.center.y + collectionView.contentOffset.y)
Md. Ibrahim Hassan
  • 5,359
  • 1
  • 25
  • 45