使用tableView.deleteRows(at :, with :)崩溃 [英] Crash with tableView.deleteRows(at:, with:)

查看:89
本文介绍了使用tableView.deleteRows(at :, with :)崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发的应用程序存在不稳定的问题.我有一个在表视图中显示的轨道阵列.在此表视图中,用户可以向左滑动单个轨道,然后出现包含三个项目的菜单.其中之一是删除操作.正是由于这个动作,我才遇到问题.

I have an erratic problem with an app I'm developing. I have an array of tracks that is displayed in a table view. In this table view a user can swipe a single track left and a menu with three items appears. One of which is a delete action. It is with this action I have a problem.

该菜单是在UITableViewDelegate方法中实现的:

The menu is implemented in the UITableViewDelegate method:

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

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

I retrieve the selected row with the following statement:

selectedTrack = indexPath.row

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

Then I make a delete action with this snippet:

//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的未捕获异常终止.

The program runs fine, but if I delete two rows after another, the program crashes with the comment: libc++abi.dylib: terminating with uncaught exception of type NSException.

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

If I comment out (or delete) the line:

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

该应用程序运行正常,但是删除行的动画消失了.有趣的是,该应用程序有时可以通过"deleteRows"功能正常运行,但往往运行不正常.我已经查看了几条评论,但尚未找到正确的答案.

The app runs fine, but the animation of deleting the row is gone. The funny thing is that the app sometimes runs fine with the "deleteRows" function, but often not. I've looked at several comments, but I have not yet found the right answer.

该表没有节,因此是数据源方法:

The table has no sections, so the data source method:

numberOfSections(in: UITableView)

未实现.谁能在正确的方向帮助我?

is not implemented. Can anyone help me in the right direction?

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

Based on the advice of Vadian I tried to change the code snippet to:

    //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, however, still makes the app crash.

应用程序崩溃的问题是我使用了一种复杂而奇怪的方式来填充表视图,这通常使应用程序期望的表行数与实际的不同.现在可以运行的代码如下:

The problem with the app crash was that I used a complex and odd way to fill the table view, which ocassionally made the app expect a different number of table rows then there really were. The code that now works looks like this:

/** 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]
}

推荐答案

有两个重要规则:

  • 永远不要在 insert/move/deleteRows ... 之后立即调用 reloadData(),insert/move/delete操作会重新排列表格并进行动画处理.
  • 在更改数据源数组后始终调用插入/移动/删除行... .
  • Never call reloadData() right after insert/move/deleteRows..., the insert/move/delete operation reorders the table and does the animation.
  • Call insert/move/deleteRows... always after changing the data source array.

这篇关于使用tableView.deleteRows(at :, with :)崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆