Swift UITableView 删除所选行及其上方的任何行 [英] Swift UITableView delete selected row and any rows above it
问题描述
我的 TableView 包含代表里程日志的单元格.我需要让用户能够删除任何错误.tableview 按降序列出日志.删除顶行是可以的.删除任何其他行,我需要发出警告作为警报,如果确认,则删除所选行及其上方的所有行.这可能吗?是否有任何示例代码?
My TableView contains cells that represent a mileage log. I need to allow users that ability to delete any mistakes. The tableview lists the logs in descending order. Deleting the top row is OK. Deleting any other row I need to issue a warning as an alert and if confirmed then delete the selected row + all rows above it. Is this possible? Is there any example code anywhere?
更新
根据我到目前为止的两个答案,我做了以下....
Based on the two answers I have had so far I have done the following....
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;
}
}
}
现在我的问题是触摸删除什么都不做.树视图已正确填充.编辑按钮位于导航栏中.单击编辑",每行都会出现无条目"图标...滑动一行,会出现删除"块.点击删除,什么都没有...!我错过了什么?
Now my problem is that the touching Delete does nothing. The treeview is correctly populated. The Edit button is in the navbar. Click Edit and the 'no entry' icon appear on each row... slide a row and the Delete block appears. Click delete and nothing...! What have I missed out?
最终工作修复
// 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;
}
}
推荐答案
除了增强使用编辑操作之外,这是一个简单的解决方案.
Apart from the enhancement to use edit actions this is the easy solution.
首先不要碰委托方法didChangeObject
.
保持原状.在托管对象上下文中进行更改后调用它,其工作方式类似于 MVC 模式中的视图.
First of all do not touch the delegate method didChangeObject
.
Leave it as it is. It is called after making changes in the managed object context and works like the view in the MVC pattern.
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
switch type {
case .insert:
tableView.insertRows(at: [newIndexPath!], with: .fade)
case .delete:
tableView.deleteRows(at: [indexPath!], with: .fade)
case .update:
self.configureCell(tableView.cellForRow(at: indexPath!)!, atIndexPath: indexPath!)
case .move:
tableView.deleteRows(at: [indexPath!], with: .fade)
tableView.insertRows(at: [newIndexPath!], with: .fade)
}
}
在 commitEditingStyle
中插入删除行的代码,其工作方式类似于 MVC 模式中的模型.该代码删除当前部分中上方选定行中的所有行.
Insert the code to delete the rows in commitEditingStyle
which works like the model in the MVC pattern. The code deletes all rows from the selected row above in the current section.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
switch editingStyle {
case .delete:
let context = fetchedResultsController.managedObjectContext
let section = indexPath.section
let currentRow = indexPath.row
for index in 0...currentRow {
let indexPathToDelete = IndexPath(row: index, section: section)
let objectToDelete = fetchedResultsController.object(at: indexPathToDelete) as! NSManagedObject
context.delete(objectToDelete)
}
do {
try context.save()
} catch {
print(error)
}
case .insert, .none: break
}
}
这篇关于Swift UITableView 删除所选行及其上方的任何行的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!