使用tableView.deleteRows崩溃(at:,with :)

时间:2017-01-07 19:57:48

标签: ios uitableview

我正在开发的应用程序存在一个不稳定的问题。我有一个在表视图中显示的轨道数组。在此表格视图中,用户可以向左滑动单个曲目,并显示包含三个项目的菜单。其中一个是删除操作。通过这个动作我遇到了问题。

菜单在UITableViewDelegate方法中实现:

override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]?

我使用以下语句检索所选行:

selectedTrack = indexPath.row

然后我使用此代码段进行删除操作:

//Delete the selected track
let deleteAction = UITableViewRowAction(style: .default, title: deleteTitle) { (action, indexPath) -> Void in

    if self.savedTracks.count > 0 {
        self.newTrack = self.savedTracks[self.selectedTrack]
        let albumName = (self.newTrack?.trackName)! + String(describing: (self.newTrack?.trackDate)!)
        self.savedTracks.remove(at: self.selectedTrack)
        self.trackOverviewTable.deleteRows(at: [indexPath], with: .fade)
        self.photoStore.deleteAlbumFromDeletedTrack(with: albumName)
        self.trackOverviewTable.reloadData()
        self.saveTracksToDisk()
    } else {
        self.trackOverviewTable.reloadData()
    }

程序运行正常,但是如果我一个接一个地删除两行,程序会崩溃并注释:libc ++ abi.dylib:以NSException类型的未捕获异常终止。

如果我注释掉(或删除)该行:

self.trackOverviewTable.deleteRows(at: [indexPath], with: .fade)

应用程序运行正常,但删除行的动画消失了。有趣的是,应用程序有时会运行" deleteRows"功能,但往往不是。我看过几条评论,但我还没有找到正确答案。

该表没有部分,因此数据源方法:

numberOfSections(in: UITableView)

未实施。 任何人都可以帮助我朝着正确的方向前进吗?

根据Vadian的建议,我尝试将代码段更改为:

    //Delete the selected track
    let deleteAction = UITableViewRowAction(style: .default, title: deleteTitle) { (action, indexPath) -> Void in

        self.trackOverviewTable.beginUpdates()
        self.newTrack = self.savedTracks[self.selectedTrack]
        let albumName = (self.newTrack?.trackName)! + String(describing: (self.newTrack?.trackDate)!)
        self.savedTracks.remove(at: indexPath.row)
        self.photoStore.deleteAlbumFromDeletedTrack(with: albumName)
        self.saveTracksToDisk()
        self.trackOverviewTable.deleteRows(at: [indexPath], with: .fade)
        self.trackOverviewTable.endUpdates()
    }

然而,这仍然会使应用程序崩溃。

应用程序崩溃的问题在于我使用了一种复杂而奇怪的方式来填充表视图,这使得应用程序期望获得不同数量的表行。现在可以使用的代码如下所示:

/** This method provides the actions when the user swipes a row left. The method first checks what sort of track it is. If the track is an emptyTrack or 
 instructionTrack, there will be no action that can be chosen. When the track is a regular recorded track the method will provide the user with the option
 to post the selected track or delete the track.
*/
override func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

    let faultTitle = NSLocalizedString("No tracks.", comment: "The message in the selection when there are no tracks.")
    let postTitle = NSLocalizedString("Post track.", comment: "The message in the selection for posting the track on social media.")
    let deleteTitle = NSLocalizedString("Delete.", comment: "The message in the selection for deleting the track.")

    selectedTrack = indexPath.row
    let selectedTrackName = self.savedTracks[selectedTrack].trackName

    //When the user swiped the empty or instruction track, only provide the fault action.
    guard (selectedTrackName != self.emptyString) && (selectedTrackName != self.instructionString) else {
        //The user selected the empty or instruction track, the action cannot be executed.
        let faultAction = UITableViewRowAction(style: .normal, title: faultTitle, handler: { (action, indexPath) -> Void in

            self.trackOverviewTable.reloadData() //Simply reload the table view after the action button is hit.
        })

        self.trackOverviewTable.cellForRow(at: indexPath)?.isSelected = false
        return [faultAction]
    }

    //The post action first checks whether the user had bought the fll version.
    //If the full version is bought the action moves to the posting menu.
    let postAction = UITableViewRowAction(style: .normal, title: postTitle) { (action, indexPath) -> Void in

        //The message when the full version has not been bought
        let alertTitle = NSLocalizedString("Upgrade @Tracker", comment: "The user doesn't have the full capabilities.")
        let alertMessage = NSLocalizedString("Do you want to upgrade @Tracker to unlock all functionalities?", comment: "Ask the user to buy the functionalities.")

        //The new track will now be set to be the selected track so it can be transfered to
        //the posting view controller
        self.newTrack = self.savedTracks[self.selectedTrack]

        if FULL_FUNCTIONALITY == true {
            self.performSegue(withIdentifier: "postTrack", sender: self)
        } else {
            //Allow the user to post the track if the full functionality is bought
            self.alertMessage(title: alertTitle, message: alertMessage, action: "Buy")
        }
    }

    //Delete the selected track
    let deleteAction = UITableViewRowAction(style: .default, title: deleteTitle) { (action, indexPath) -> Void in

        //First set the warning message when the user wants to delete the track.
        let title = NSLocalizedString("Warning", comment: "Ask alert the user that he is going to delete the track.")
        let message = NSLocalizedString("Are you sure you want to delete the track?", comment: "Ask the user if he's sure.")
        let alertYes = NSLocalizedString("Yes", comment: "Ja")
        let alertNo = NSLocalizedString("No", comment: "Nee")
        let alertMessage = UIAlertController.init(title: title, message: message, preferredStyle: .alert)

        let okAction = UIAlertAction(title: alertYes, style: .default, handler: { (action) in
            self.deleteTrackFromTable(at: indexPath)

            var indexPathSelected = indexPath
            indexPathSelected.row = 0
            self.trackOverviewTable.scrollToRow(at: indexPathSelected, at: .none, animated: true)
            self.presentingViewController?.dismiss(animated: true, completion: nil)
        })
        alertMessage.addAction(okAction)

        let notOkAction = UIAlertAction(title: alertNo, style: .default, handler: { (action) in
            self.trackOverviewTable.reloadData()
            self.presentingViewController?.dismiss(animated: true, completion: nil)
        })
        alertMessage.addAction(notOkAction)

        self.present(alertMessage, animated: true, completion: nil)
    }

    postAction.backgroundColor = MENU_COLOR_1

    self.trackOverviewTable.cellForRow(at: indexPath)?.isSelected = false
    return [deleteAction, postAction, analysisAction]
}

1 个答案:

答案 0 :(得分:11)

有两个重要规则:

  • 永远不要在reloadData()之后立即调用insert/move/deleteRows...,插入/移动/删除操作会重新排序表并执行动画。
  • 在更改数据源数组后,始终调用insert/move/deleteRows...