Swift UITableView删除选定的行及其上方的任何行
我的TableView包含代表里程记录的单元格。 我需要允许用户删除任何错误。 tableview按降序列出日志。 删除顶行是可以的。 删除我需要发出警告的任何其他行作为警报,如果确认,则删除所选行+上面的所有行。 这可能吗? 在任何地方都有代码吗?
UPDATE
根据我迄今为止的两个答案,我已经完成了以下工作….
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; } }
除了使用编辑操作的增强function外,这是一个简单的解决方案。
首先不要触摸委托方法didChangeObject
。
保持原状。 它在托管对象上下文中进行更改后调用,并且像MVC模式中的视图一样工作。
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) } }
插入代码以删除commitEditingStyle
中的行,其行为类似于MVC模式中的模型。 代码将删除当前部分中上面所选行的所有行。
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { switch editingStyle { case .Delete: let context = fetchedResultsController.managedObjectContext let section = indexPath.section let currentRow = indexPath.row for index in 0...currentRow { let indexPathToDelete = NSIndexPath(forRow: index, inSection: section) let objectToDelete = fetchedResultsController.objectAtIndexPath(indexPathToDelete) as! NSManagedObject context.deleteObject(objectToDelete) } do { try context.save() } catch let error as NSError { print(error) } case .Insert, .None: break } }
这就是我相信你能做的:)
知道你已经使用fetchedResultsController sort predicate :)对数据进行了排序现在你所要做的就是删除用户选择的从0到行的所有行:)
因为你只有一个部分,并且所有单元格的索引路径中的部分都为“0”:)你所要做的就是从0迭代到所选单元格的行号:)
创建一个索引路径,迭代值为行,“0”为部分:)从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 }()
希望我回答你的问题:)有疑问吗? 在下面留下评论:)快乐编码伙伴:)
您可以使用UITableViewDelegate协议的editActionsForRowAtIndexPath
方法来实现,该方法自iOS 8.0开始提供。
回报价值
一组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)] } }
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) }
}