2016-04-26 2 views
1

My TableView содержит ячейки, представляющие журнал пробега. Мне нужно разрешить пользователям удалять любые ошибки. В таблице отображается список журналов в порядке убывания. Удаление верхней строки в порядке. При удалении любой другой строки мне нужно выпустить предупреждение в качестве предупреждения, и если оно подтверждено, удалите выбранную строку + все строки над ней. Это возможно? Есть ли какой-нибудь пример кода в любом месте?Swift UITableView удаляет выбранную строку и любые строки над ней

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; 
     } 
    } 

} 

Теперь моя проблема заключается в том, что прикосновение Delete ничего не делает. Дерево правильно заполнено. Кнопка «Редактировать» находится на панели навигации. Нажмите «Изменить», и значок «нет записи» появится в каждой строке ... сдвиньте строку и появится блок «Удалить». Нажмите «Удалить» и ничего ...! Что я пропустил?

FINAL РАБОЧАЯ FIX

// 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; 
    } 
} 
+0

Зачем вам нужен пример для этого приятеля ?? просто удалите строки из источника данных и перезагрузите tableview :) thats all :) –

+0

Я новичок в iOS и быстро, поэтому мне может быть что-то не хватает ... TableView имеет кнопку редактирования на navbar. Пользователь нажимает на редактирование, затем выбирает строку и клики удаляет.Это приведет к удалению этой строки. Все до сих пор ... что мне нужно, это дополнение к этому, где код не только удалит выбранную строку, но и все строки над ней. – Mych

+0

Вы не удаляете из uitableview, вы удаляете из источника данных, а затем вызываете tableView.reloadData(). – oyalhi

ответ

4

Помимо улучшения использования действий редактирования это простое решение.

Прежде всего не прикасайтесь к методу делегата 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 
    } 
    } 
+0

Vadian, большое спасибо ... Я разместил обновление, которое теперь включает предупреждение, если будет удалено более одной строки ... Спасибо Michaël за помощь в этом .... – Mych

1

Вы можете сделать это с помощью метода editActionsForRowAtIndexPathUITableViewDelegate протокола, доступного так прошивкой 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)] 
    }    
} 
+0

Michaël ... см. Мой код, который я основал на Sandeep и вашем коде. У меня что-то отсутствует, так как действие удаления не запускается. – Mych

+0

Просто посмотрев на это, позвольте мне посмотреть на него :) –

+0

Где вы работаете с удалением элемента в вашем представлении таблицы? Вы должны реализовать метод 'commitEditingStyle' или' editActionsForRowAtIndexPath' для его обработки. –

2

Вот что я считаю, что вы можете сделать :)

Зная, что вы уже отсортирован ваши данные, используя fetchedResultsController рода предикат :) теперь все вам нужно удалить все строки из 0 в строку, которую выбрал пользователь :)

Поскольку у вас есть только один раздел, и вся ячейка будет иметь раздел как «0» в своем указательном пути :) a вам нужно будет перебирать от 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) 
     } 

EDIT

В соответствии с вашим обновленным вопросом никто из делегатов контроллера fetchedResults не вызван.

Это потому, что вы не сказали получателю диспетчеру, которому сообщать, когда что-то происходит в БД. То, что я имел в виду, вы не установили делегат контроллера 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 

    }() 

Надежда Я answerred ваш вопрос :) Есть сомнения ? Оставить комментарий ниже :) Счастливое кодирование buddy :)

+0

@Mych: Sure buddy :) Я сосредоточился только на удалении управляемых объектов из основных данных :) Вы можете использовать код Michaël Azevedo для обработки UI :) Все самое лучшее :) –

+0

Sandeep ... см. Мой код, что я основаны на Михаэле и вашем коде. У меня что-то отсутствует, так как действие удаления не запускается. – Mych

+0

Что вы подразумеваете под удалением action not trigerred buddy ??? ваш делегат didChangeObject получает вызов? Отображается ли ваше предупреждение ??? –

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) 

} 

}

+0

Насколько я вижу это удалит строку из TableView, но не из Core Data (datasource) – Mych

+0

yes Mych, он удалит строки и данные из массива, а не Core Data. вы должны обновить свои основные данные –

Смежные вопросы