UITableView拖放外表=崩溃

好的

我的拖放function几乎可以很好地工作。 我longPress一个单元格,它顺利地允许我将按下的单元格移动到另外两个单元格之间的新位置。 该表进行调整,更改保存到核心数据。 大!

我的问题是,如果我将单元格拖到表格底部单元格下方,即使我不放弃(取消按下)单元格…应用程序崩溃。 如果我慢慢拖动,当细胞穿过最后一个细胞的y中心时真的会崩溃……所以我认为这是一个与获取位置的快照相关的问题。 不太重要但可能相关的是,如果我长时间按下最后一个单元格下面的值,它也会崩溃。

拖放运行一个switch语句,该语句根据状态运行三组代码中的一组:

  • 新闻开始时的一个案例
  • 拖动单元格的一种情况
  • 当用户放开单元格时的一种情况

我的代码改编自本教程:

拖放教程

我的代码:

func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) { let longPress = gestureRecognizer as! UILongPressGestureRecognizer let state = longPress.state var locationInView = longPress.locationInView(tableView) var indexPath = tableView.indexPathForRowAtPoint(locationInView) struct My { static var cellSnapshot : UIView? = nil } struct Path { static var initialIndexPath : NSIndexPath? = nil } let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! CustomTableViewCell; var dragCellName = currentCell.nameLabel!.text var dragCellDesc = currentCell.descLabel.text //Steps to take a cell snapshot. Function to be called in switch statement func snapshotOfCell(inputView: UIView) -> UIView { UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0) inputView.layer.renderInContext(UIGraphicsGetCurrentContext()) let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage UIGraphicsEndImageContext() let cellSnapshot : UIView = UIImageView(image: image) cellSnapshot.layer.masksToBounds = false cellSnapshot.layer.cornerRadius = 0.0 cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0) cellSnapshot.layer.shadowRadius = 5.0 cellSnapshot.layer.shadowOpacity = 0.4 return cellSnapshot } switch state { case UIGestureRecognizerState.Began: //Calls above function to take snapshot of held cell, animate pop out //Run when a long-press gesture begins on a cell if indexPath != nil && indexPath != nil { Path.initialIndexPath = indexPath let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell! My.cellSnapshot = snapshotOfCell(cell) var center = cell.center My.cellSnapshot!.center = center My.cellSnapshot!.alpha = 0.0 tableView.addSubview(My.cellSnapshot!) UIView.animateWithDuration(0.25, animations: { () -> Void in center.y = locationInView.y My.cellSnapshot!.center = center My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05) My.cellSnapshot!.alpha = 0.98 cell.alpha = 0.0 }, completion: { (finished) -> Void in if finished { cell.hidden = true } }) } case UIGestureRecognizerState.Changed: if My.cellSnapshot != nil && indexPath != nil { //Runs when the user "lets go" of the cell //Sets CG Y-Coordinate of snapshot cell to center of current location in table (snaps into place) var center = My.cellSnapshot!.center center.y = locationInView.y My.cellSnapshot!.center = center var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate) var context: NSManagedObjectContext = appDel.managedObjectContext! var fetchRequest = NSFetchRequest(entityName: currentListEntity) let sortDescriptor = NSSortDescriptor(key: "displayOrder", ascending: true ) fetchRequest.sortDescriptors = [ sortDescriptor ] //If the indexPath is not 0 AND is not the same as it began (didn't move)... //Update array and table row order if ((indexPath != nil) && (indexPath != Path.initialIndexPath)) { swap(&taskList_Cntxt[indexPath!.row], &taskList_Cntxt[Path.initialIndexPath!.row]) tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!) toolBox.updateDisplayOrder() context.save(nil) Path.initialIndexPath = indexPath } } default: if My.cellSnapshot != nil && indexPath != nil { //Runs continuously while a long press is recognized (I think) //Animates cell movement //Completion block: //Removes snapshot of cell, cleans everything up let cell = tableView.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell! cell.hidden = false cell.alpha = 0.0 UIView.animateWithDuration(0.25, animations: { () -> Void in My.cellSnapshot!.center = cell.center My.cellSnapshot!.transform = CGAffineTransformIdentity My.cellSnapshot!.alpha = 0.0 cell.alpha = 1.0 }, completion: { (finished) -> Void in if finished { Path.initialIndexPath = nil My.cellSnapshot!.removeFromSuperview() My.cellSnapshot = nil } })//End of competion block & end of animation }//End of 'if nil' }//End of switch }//End of longPressGestureRecognized 

潜在的罪魁祸首

我的猜测是,问题与细胞一旦低于最后一个细胞就无法获得坐标有关。 它并非真正浮动,它会不断地设置其相对于其他单元格的位置。 我认为解决方案将是一个if语句,当没有单元格引用某个位置时,它会做一些神奇的事情。 但是什么!?! 为每种情况添加零检查由于某种原因不起作用。

明确陈述的问题

如何避免崩溃并处理拖动的单元格拖到最后一个单元格下方的事件?

崩溃截图

超出范围

丑陋的

您似乎只需要执行抢先检查,以确保您的indexPath不是nil:

 var indexPath = tableView.indexPathForRowAtPoint(locationInView) if (indexPath != nil) { //Move your code to this block } 

希望有所帮助!

您没有说明崩溃发生在代码中的哪个位置,这使得确定发生的事情变得更加困难。 在exception上设置断点以确定哪条线是罪魁祸首。 为此,请使用XCode中断点列表左下角的“+”。

我认为主要问题是indexPath。 有几个问题:

  1. 您正在使用indexPath,即使它可能是nil,在此行中:

     let currentCell = tableView.cellForRowAtIndexPath(indexPath!) as! CustomTableViewCell; 
  2. indexPath可能无效,即使它不是nil。 检查其部分和行成员是否与NSNotFound不同。

最后,我一直在使用一个预制的开源UITableView子类,它可以为您完成所有移动操作,因此您不必再自己实现它了。 它还负责自动滚动,你还没有考虑过它。 直接使用它,或将其用作代码的灵感: https : //www.cocoacontrols.com/controls/fmmovetableview