0

I have custom CollectionView cell and on button tap I am calling closure which is implemented under cellForItem

below is code

  cell.closeImageTappped  = { [weak self] cell in
        guard let strongSelf = self else {
            return
        }


        if let objectFirst = strongSelf.selectedFiles.first(where: {$0.fileCategory == cell.currentSelectedCellType && $0.fileName == cell.currentObject?.fileName}) {
            cell.imgPicture.image = nil

            cell.imgPlusPlaceHolder.isHidden = false
            objectFirst.removeImageFromDocumentDirectory()
            strongSelf.selectedFiles.remove(at: strongSelf.selectedFiles.index(where: {$0.fileName == objectFirst.fileName})!)
            strongSelf.arraySource[indexPath.section].rows.remove(at: indexPath.row)
            strongSelf.collectionViewSelectFile.performBatchUpdates({
                strongSelf.collectionViewSelectFile.deleteItems(at: [indexPath])

            }, completion: nil)
        }

    }

App Crashes in some cases like if I am pressing close multiple cells too fast

Crash here

strongSelf.arraySource[indexPath.section].rows.remove(at: indexPath.row)

fatal error: Index out of range

When I check rows

▿ 11 elements - 0 : 0 - 1 : 1 - 2 : 2 - 3 : 3 - 4 : 4 - 5 : 5 - 6 : 6 - 7 : 7 - 8 : 8 - 9 : 8 - 10 : 8

While IndexPath is

po indexPath ▿ 2 elements - 0 : 0 - 1 : 11

If I get indexPath like this it shows me correct IndexPath

self?.collectionViewSelectFile.indexPath(for: cell) ▿ Optional<IndexPath> ▿ some : 2 elements - 0 : 0 - 1 : 9

But Why IndexPath is different then self?.collectionViewSelectFile.indexPath(for: cell)

Prashant Tukadiya
  • 15,838
  • 4
  • 62
  • 98
  • Unrelated but what's that weak-self, strong-self back and forth for? If `self` was ever `nil` you would not be able to push the button because it would be off-screen. It's absolutely safe to use `[unowned self]`. Regarding the issue I guess that the index paths are not updated properly after deleting a cell. – vadian Nov 08 '17 at 09:05
  • Where does `indexPath` come from? Is it from outside the closure? – Paulw11 Nov 08 '17 at 09:10
  • @vadian **Unrelated but what's that weak-self, strong-self back and forth for?** Yo are correct I thought Suppose Long running task is ongoing inside clouser before it get executed user press back button so I used weak self , **I guess that the index paths are not updated properly after deleting a cell** Any suggestion ? – Prashant Tukadiya Nov 08 '17 at 09:12
  • @Paulw11 As I mentioned in question this closure is in `cellForItem` delegate method – Prashant Tukadiya Nov 08 '17 at 09:13
  • @Paulw11 Is my code for delete is correct ? , `strongSelf.collectionViewSelectFile.performBatchUpdates({` is async task and pressing multiple times before execution finished is that problem ? – Prashant Tukadiya Nov 08 '17 at 09:17
  • @vadian Yes , There is problem to do that because My Cells are fixed static cells so I Just used `Enum` as a source and setup according to case in `cellForItem`, and the last Row can be one or more in numbers you can see my question where i have printed rows array – Prashant Tukadiya Nov 08 '17 at 09:23
  • I deleted the other comment because Paul has provided a suitable answer. – vadian Nov 08 '17 at 09:23

1 Answers1

1

indexPath comes from outside your closure, so its value is captured at the time that the closure is assigned to the cell. Let's say you have 10 items in your array and you delete the 9th item. Your array now has 9 items, but the cell that is displaying what was the 10th item (but is now the 9th item - index 8 in the array) still has 9 for indexPath.row, not 8, so you get an array bounds violation when you try and delete the last row.

To avoid this issue, you can use indexPath(for:) on your collection view inside the closure in order to determine the current indexPath for the cell:

cell.closeImageTappped  = { [weak self] cell in

        guard let strongSelf = self else {
            return
        }


        if let objectFirst = strongSelf.selectedFiles.first(where: {$0.fileCategory == cell.currentSelectedCellType && $0.fileName == cell.currentObject?.fileName}), 
           let indexPath = collectionView.indexPath(for: cell) {
            cell.imgPicture.image = nil

            cell.imgPlusPlaceHolder.isHidden = false
            objectFirst.removeImageFromDocumentDirectory()
            strongSelf.selectedFiles.remove(at: strongSelf.selectedFiles.index(where: {$0.fileName == objectFirst.fileName})!)
            strongSelf.arraySource[indexPath.section].rows.remove(at: indexPath.row)
            strongSelf.collectionViewSelectFile.performBatchUpdates({
                strongSelf.collectionViewSelectFile.deleteItems(at: [indexPath])

            }, completion: nil)
        }

    }
Paulw11
  • 108,386
  • 14
  • 159
  • 186
  • Thank you , I have already found solution as mentioned in question , And I just want to know the reason behind that you have explained superbly :D – Prashant Tukadiya Nov 08 '17 at 09:27