iosswifttableviewuilongpressgesturerecognizer

UILongPressGestureRecognizer crashes in tableview when dragged to top of view - SWIFT


I am using a UILongPressGestureRecognizer for each cell in a table view that has two sections/section headers. When I do a long press on a cell in my first section and drag to the top of the table view my app crashes. (I have disabled the longPressGestureRecognizer for the second section). I get an exception point at the line below,

var indexPath: NSIndexPath? = self.routeTableView.indexPathForRowAtPoint(location)!

I receive this error in the debugger,

fatal error: unexpectedly found nil while unwrapping an Optional value

my code is below,

func longPressGestureRecognized(gesture: UILongPressGestureRecognizer){
    
    var state: UIGestureRecognizerState = gesture.state
    
    var location:CGPoint = gesture.locationInView(self.routeTableView)
    var indexPath: NSIndexPath? = self.routeTableView.indexPathForRowAtPoint(location)!
    
    if indexPath == nil {
        return
    }
    
    if indexPath?.section != 1 {
        
        switch(state){
            case UIGestureRecognizerState.Began:
                sourceIndexPath = indexPath
                
                var cell: UITableViewCell = self.routeTableView .cellForRowAtIndexPath(indexPath!)! as! RouteSelectionCell
                
                //take a snapshot of the selected row using helper method
                snapshot = customSnapshotFromView(cell)
                
                //add snapshot as subview, centered at cell's center
                var center: CGPoint = cell.center
                snapshot?.center = center
                snapshot?.alpha  = 0.0
                self.routeTableView.addSubview(snapshot!)
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    center.y = location.y
                    self.snapshot?.center = center
                    self.snapshot?.transform = CGAffineTransformMakeScale(1.05, 1.05)
                    self.snapshot?.alpha = 0.98
                    cell.alpha = 0.0
                }, completion: { (finished) in
                    cell.hidden = true
                })
                
            case UIGestureRecognizerState.Changed:
                var center: CGPoint = snapshot!.center
                center.y = location.y
                snapshot?.center = center
                
                
                //is destination valid and is it different form source?
                if indexPath != sourceIndexPath{
                    //update data source
                    self.messageOptions.exchangeObjectAtIndex(indexPath!.row, withObjectAtIndex: sourceIndexPath!.row)
                    //move the row
                    self.routeTableView.moveRowAtIndexPath(sourceIndexPath!, toIndexPath: indexPath!)
                    //and update source so it is in sync with UI changes
                    sourceIndexPath = indexPath
                }
                
            default:
                //clean up
                let cell: UITableViewCell = routeTableView.cellForRowAtIndexPath(sourceIndexPath!)!
                cell.alpha = 0.0
                cell.hidden = false
                
                UIView.animateWithDuration(0.25, animations: { () -> Void in
                    
                    self.snapshot?.center = cell.center
                    self.snapshot?.transform = CGAffineTransformIdentity
                    self.snapshot?.alpha = 0.0
                    //undo fade out
                    cell.alpha = 1.0
                    
                }, completion: { (finished) in
                    self.sourceIndexPath = nil
                    self.snapshot?.removeFromSuperview()
                    self.snapshot = nil
                })
                break
        }
    }
}

Anyone have any suggestions on how to avoid this issue?


Solution

  • When you call self.routeTableView.indexPathForRowAtPoint(location)! you are force unwrapping the value that comes back, and in this case, its nil. This is where you are crashing.

    Your code should do something like this:

    if let indexPath = self.routeTableView.indexPathForRowAtPoint(location) {
      //do normal code
    } else {
      //the point didn't map back to a cell! Maybe it was on the header or footer? 
      //Handle this case differently.
    }
    

    Also, I found this answer that might help:

    indexPathForRowAtPoint returns nil only for first cell in a uitableview