Search code examples
iosswiftoffsetcollectionviewcgpoint

Select cell on scroll in collectionView by default


I have a custom collection view layout which seems to produce some bugs. Or maybe I'm missing something. Layout looks like this and it's from here:

enter image description here

My problem is I can't pass the indexPath of the centered cell to pink button, call of centerCellIndexPath produces nil. I also tried to preselect the cell in the layout itself, like so:

 override open func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint {
    // item's setup ......
    // ...................

    let finalPoint = CGPoint(x: newOffsetX, y: proposedContentOffset.y)

    // Select cell in collectionView by Default
    let updatePoint = CGPoint(x: newOffsetX, y: 180)
    let indexPath = collectionView!.indexPathForItem(at: updatePoint)
    self.collectionView!.selectItem(at: indexPath, animated: false, scrollPosition: [])

    return finalPoint
}

But it doesn't work. I have to manually select the cell by swiping up, which is pretty unclear for user. How should I select the cell without tapping and swiping?

Any advice will be appreciated

UPD: After a night without a sleep finally managed it thanks to Vollan

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    let position = 2 * scrollView.contentOffset.x / collectionView.superview!.frame.width

    selectedItem = Int(round(position))


}

UPD: But the best just got better. Converted answer from linked to Swift version

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.

So it takes 2 lines:

 let centerPoint = self.view.convert(view.center, to: collectionView)
 let indexPath = collectionView.indexPathForItem(at: centerPoint)
 -> save media[indexPath.row]

And that was basically what Sachin Kishore suggested, but I didn't understand him at first

I kind of like the idea with dynamic indexPath but it takes some rounding, which is not reliable. So I think convert and indexPathForItem is the best here


Solution

  • If you were to put the logic into the scrollViewDidScroll you can update an indexPath dynamically.

    let position = scrollView.contentOffset.x/(cellSize+spacing)
    currenIndexPath.item = position
    

    This should give you the current cells indexPath at all times.