Swift UITableView删除选定的行及其上方的任何行

时间:2016-04-26 09:55:20

标签: ios swift uitableview

我的TableView包含代表里程记录的单元格。我需要允许用户删除任何错误。 tableview按降序列出日志。删除顶行是可以的。删除我需要发出警告的任何其他行作为警报,如果确认,则删除所选行+上面的所有行。这可能吗?在任何地方都有代码吗?

更新

根据我迄今为止所做的两个答案,我已经完成了以下工作....

import UIKit
import CoreData

class MileageLogsTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {

    @IBOutlet var milageLogTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        do {
            try fetchedResultsController.performFetch()
        } catch {
            let fetchError = error as NSError
            print("Unable to fetch MileageLog")
            print("\(fetchError), \(fetchError.localizedDescription)")
        }

        // Display an Edit button in the navigation bar for this view controller.
        self.navigationItem.leftBarButtonItem = self.editButtonItem()
    }

    // MARK: - Table view data source

    private lazy var fetchedResultsController: NSFetchedResultsController = {
        // Initialize Fetch Request
        let fetchRequest = NSFetchRequest(entityName: "MileageLog")

        // Add Sort Descriptors
        let dateSort = NSSortDescriptor(key: "tripDate", ascending: false)
        let mileSort = NSSortDescriptor(key: "startMileage", ascending: false)
        fetchRequest.sortDescriptors = [dateSort, mileSort]

        let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedObjectContext = delegate.managedObjectContext

        // Initialize Fetched Results Controller
        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: "rootCache")

        //ADDED AS PER ANSWER FROM SANDEEP
    fetchedResultsController.delegate = self

        return fetchedResultsController

    }()


    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        if let sections = fetchedResultsController.sections {
            return sections.count
        }

        return 0
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let sections = fetchedResultsController.sections {
            let sectionInfo = sections[section]
            return sectionInfo.numberOfObjects
        }

        return 0
    } 

     override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("MileageLogCell") as! MileageTableViewCell

        // Fetch MileageLog
        if let mileageLog = fetchedResultsController.objectAtIndexPath(indexPath) as? MileageLog {
            //format date as medium style date
            let formatter = NSDateFormatter()
            formatter.dateStyle = .MediumStyle
            let logDateString = formatter.stringFromDate(mileageLog.tripDate!)
            //format NSNumber mileage to string
            let mileageInt:NSNumber = mileageLog.startMileage!
            let mileageString = String(mileageInt)

            cell.lb_LogDate.text = logDateString
            cell.lb_LogMileage.text = mileageString
            cell.lb_LogStartLocation.text = mileageLog.startLocation
            cell.lb_LogDestination.text = mileageLog.endLocation
        }
        return cell
     }


    // Override to support conditional editing of the table view.
    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        return true
    }

    // MARK: Fetched Results Controller Delegate Methods
    func controllerWillChangeContent(fetchedResultsController: NSFetchedResultsController) {
        tableView.beginUpdates()
    }

    func controllerDidChangeContent(fetchedResultsController: NSFetchedResultsController) {
        tableView.endUpdates()
    }

    func controller(fetchedResultsController: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
        switch (type) {
            case .Insert:
                break;
            case .Delete:

                let context = fetchedResultsController.managedObjectContext
                if let indexPath = indexPath {

                    if indexPath.row == 0 {
                        //this is the top (first row)
                        // Deleting without warning
                        let objectToDelete = fetchedResultsController.objectAtIndexPath(indexPath) as! NSManagedObject
                        context.deleteObject(objectToDelete)

                        do {
                            try context.save()
                            self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)

                        } catch {
                            print(error)
                        }
                        self.tableView.reloadData();

                    } else {
                        //we are deleted a row that is not the top row
                        // we need to give a warning and if acknowledged then delele all rows from the selected row and all rows above it

                        let alertController = UIAlertController(title: nil, message: "Are you sure? This will remove this and all logs above it.", preferredStyle: .Alert)
                        let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in

                        }
                        alertController.addAction(cancelAction)
                        let deleteAction = UIAlertAction(title: "Delete", style: .Default) { (action) in

                            for deleteindex in 0 ... indexPath.row {
                                let deleteIndexPath = NSIndexPath(forRow: deleteindex, inSection: 0)
                                let objectToDelete = self.fetchedResultsController.objectAtIndexPath(deleteIndexPath) as! NSManagedObject
                                context.deleteObject(objectToDelete)

                                do {
                                    try context.save()
                                    self.tableView.deleteRowsAtIndexPaths([deleteIndexPath], withRowAnimation: .Fade)

                                } catch {
                                    print(error)
                                }
                            }
                            self.tableView.reloadData();
                        }
                        alertController.addAction(deleteAction)

                        // Dispatch on the main thread
                        dispatch_async(dispatch_get_main_queue()) { 
                            self.presentViewController(alertController, animated: true, completion:nil)
                        }

                    }
                }
                break;
            case .Update:
                break;
            case .Move:
                break;
        }
    }

}

现在我的问题是触摸删除什么也没做。树视图已正确填充。 “编辑”按钮位于导航栏中。点击修改,然后点击“否”条目'图标出现在每一行...滑动一行,然后出现删除块。点击删除,没有......我错过了什么?

最终工作修补

// Override to support editing the table view.
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

    switch editingStyle {

     case .Delete:
        let context = fetchedResultsController.managedObjectContext
            if indexPath.row == 0 {
                //this is the top (first row)
                // Deleting without warning
                let indexPathToDelete = NSIndexPath(forRow: 0, inSection: 0)
                let objectToDelete = fetchedResultsController.objectAtIndexPath(indexPathToDelete) as! NSManagedObject
                context.deleteObject(objectToDelete)

                do {
                    try context.save()
                    //self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)

                } catch {
                    print(error)
                }
                //self.tableView.reloadData();

            } else {
                //we are deleted a row that is not the top row
                // we need to give a warning and if acknowledged then delele all rows from the selected row and all rows above it

                let alertController = UIAlertController(title: nil, message: "Are you sure? This will remove this and all logs above it.", preferredStyle: .Alert)
                let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in

                }
                alertController.addAction(cancelAction)
                let deleteAction = UIAlertAction(title: "Delete", style: .Default) { (action) in

                    for deleteindex in 0 ... indexPath.row {
                        let deleteIndexPath = NSIndexPath(forRow: deleteindex, inSection: 0)
                        let objectToDelete = self.fetchedResultsController.objectAtIndexPath(deleteIndexPath) as! NSManagedObject
                        context.deleteObject(objectToDelete)

                    }

                    do {
                        try context.save()

                    } catch {
                        print(error)
                    }
                }
                alertController.addAction(deleteAction)

                // Dispatch on the main thread
                dispatch_async(dispatch_get_main_queue()) {
                    self.presentViewController(alertController, animated: true, completion:nil)
                }

            }
        break;

    default :
        return
    }

}

// MARK: Fetched Results Controller Delegate Methods
func controllerWillChangeContent(controller: NSFetchedResultsController) {
    tableView.beginUpdates()
}

func controllerDidChangeContent(controller: NSFetchedResultsController) {
    tableView.endUpdates()
}

func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
    switch type {
    case .Insert:
        break;
    case .Delete:
        tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade)
    case .Update:
        break;
    case .Move:
        break;
    }
}

4 个答案:

答案 0 :(得分:5)

除了使用编辑操作的增强功能外,这是一个简单的解决方案。

首先不要触及委托方法deploy.sh 保持原状。它在托管对象上下文中进行更改后调用,并且像MVC模式中的视图一样工作。

didChangeObject

插入代码以删除 func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case .Insert: tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) case .Delete: tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) case .Update: self.configureCell(tableView.cellForRowAtIndexPath(indexPath!)!, atIndexPath: indexPath!) case .Move: tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: .Fade) tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: .Fade) } } 中的行,其行为类似于MVC模式中的模型。代码将删除当前部分中上面所选行的所有行。

commitEditingStyle

答案 1 :(得分:2)

以下是我相信你能做的事情:)

知道你已经使用fetchedResultsController sort predicate :)对数据进行了排序现在你所要做的就是删除用户选择的从0到行的所有行:)

因为您只有一个部分,所有单元格的部分都为" 0"在他们的索引路径:)你要做的就是从0迭代到所选单元格的行号:)

创建一个索引路径,其迭代值为行和" 0"作为section :)从fetchedResultsController获取对象并在循环中删除它:)

完成后重新加载tableView:)

参考上一个问题:)

case .Delete:
        // Delete the row from the data source

        let context = fetchedResultsController.managedObjectContext
        for deleteindex in 0 ... indexPath.row {
                var deleteIndexPath = NSIndexPath(forRow: deleteindex, inSection: 0)
                let objectToDelete = fetchedResultsController.objectAtIndexPath(deleteIndexPath) as! NSManagedObject
               context.deleteObject(objectToDelete)
        }
        do {
          try context.save()
          self.tableView.reloadData();
        } catch {
           print(error)
        }

修改

根据您更新的问题,没有调用fetchedResults控制器委托。

那是因为,您还没有告诉fetchedResults控制器,当DB中发生某些事情时,该控制器会通知谁。我的意思是你没有将fetchedResults控制器的委托设置为自己的伙伴:)

通过修改和评论复制粘贴您自己的代码

private lazy var fetchedResultsController: NSFetchedResultsController = {
        // Initialize Fetch Request
        let fetchRequest = NSFetchRequest(entityName: "MileageLog")

        // Add Sort Descriptors
        let dateSort = NSSortDescriptor(key: "tripDate", ascending: false)
        let mileSort = NSSortDescriptor(key: "startMileage", ascending: false)
        fetchRequest.sortDescriptors = [dateSort, mileSort]

        let delegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let managedObjectContext = delegate.managedObjectContext

        // Initialize Fetched Results Controller
        let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: "rootCache")

        //set the delegate to self here. You have confirmed the fetchedResultsController delegate in interface remember ??
        fetchedResultsController.delegate = self

        return fetchedResultsController

    }()

希望我回答你的问题:)有疑问吗?在下面发表评论:)快乐的编码伙伴:)

答案 2 :(得分:1)

您可以使用自{8.0}以来的UITableViewDelegate协议editActionsForRowAtIndexPath方法进行此操作。

  

返回值

     

表示行操作的UITableViewRowAction对象数组。您提供的每个操作都用于创建用户可以点按的按钮。

     

<强>讨论

     

如果要为其中一个表行提供自定义操作,请使用此方法。当用户在一行中水平滑动时,表格视图会将行内容移到一边以显示您的操作。点击其中一个操作按钮会执行与操作对象一起存储的处理程序块。

以下是实施示例

func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {

    if indexPath.row == 0 {
        // Deleting without warning

        // The closure will be called when tapping on the action
        let deleteClosure = { (action: UITableViewRowAction!, indexPath: NSIndexPath!) -> Void in    

            // Delete the first object
            // you can animate it with the BeginUpdates... EndUpdates too             
            self.dataSourceArray.removeAtIndex(0)
            self.tableView.reloadData()
        }

        // Default style is Destructive
        return [UITableViewRowAction(style: .Default, title: "Delete", handler: deleteClosure)]
    }
    else  {
        // Deleting with warning

        // The closure will be called when tapping on the action
        let deleteClosure = { (action: UITableViewRowAction!, indexPath: NSIndexPath!) -> Void in    

            let alertController = UIAlertController(title: nil, message: Localization(kStr_NotifDisabled), preferredStyle: .Alert)
            let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action) in

            }
            alertController.addAction(cancelAction)
            let deleteAction = UIAlertAction(title: "Delete", style: .Default) { (action) in

                self.dataSourceArray(Range(start: 0, end: indexPath.row))    
                self.tableView.reloadData()
            }
            alertController.addAction(deleteAction)

            // Dispatch on the main thread
            dispatch_async(dispatch_get_main_queue()) { 
                self.presentViewController(alertController, animated: true, completion:nil)
            }
        }          
        // Default style is Destructive
        return [UITableViewRowAction(style: .Default, title: "Delete", handler: deleteClosure)]
    }            
}

答案 3 :(得分:0)

override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true

}

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
  arrRecordList.removeAtIndex(indexPath.row)    
  tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)

}

}