4

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

Max Kraev
  • 740
  • 12
  • 31
  • just asking finalPoint is the cell which is Displayed at centre ? – iOS Geek Apr 18 '18 at 05:13
  • I guess so) I can paste the full method if it'll help – Max Kraev Apr 18 '18 at 05:19
  • I can suggest using https://stackoverflow.com/a/19266183/6080920 , get the CGPoint of cell that is located in centre of CollectionView and convert that CGPoint into IndexPath and then you will get what you want in output – iOS Geek Apr 18 '18 at 05:25
  • This doesn't solve your solution, but i would personally choose to have a dynamic `indexPath` as such `var currentIndexPath: IndexPath`. Which you update (item?) value on when you swipe left or right (+ or -) – Vollan Apr 18 '18 at 05:27
  • @iOSGeek saw that, tried it, no luck. I managed to grab an index path by accessing `collectionView.subviews.first?.center`, but those paths were completely random as I saw in console – Max Kraev Apr 18 '18 at 05:35
  • @MaxKraev When do you access `centerCellIndexPath`? I have noticed that it returns `nil` only when you reach the end of the `collectionView` and it's to recycle from the first cell again. – staticVoidMan Apr 18 '18 at 05:36
  • @Vollan but how can I update it by Int if I'm scrolling for example from 1 to 5 item in one go? You mean in `scrollViewDidScroll`? – Max Kraev Apr 18 '18 at 05:37
  • @staticVoidMan tried it from different places, from `cellForRow`, from my custom methods, from `viewDidLoad`. Btw I switched off the infinite scroll – Max Kraev Apr 18 '18 at 05:39
  • 1
    @MaxKraev Sorry, i thought you were stepping when you where scrolling, but then yeah, in `scrollViewDidScroll` you can update `indexPath` every time you moved X points. `ceil(scrollView.contentOffset.x/(cellSize+spacing))` or something like that. Note that it's not tested – Vollan Apr 18 '18 at 05:41

2 Answers2

1
func viewIndexPathInCollectionView(_ cellItem: UIView, collView: UICollectionView) -> IndexPath? {
    let pointInTable: CGPoint = cellItem.convert(cellItem.bounds.origin, to: collView)
    if let cellIndexPath = collView.indexPathForItem(at: pointInTable){
        return cellIndexPath
    }
    return nil

}

call this function on button action and pass the button name , collection view name on this function

Sachin Kishore
  • 324
  • 3
  • 8
1

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.

Vollan
  • 1,887
  • 11
  • 26