2016-03-10 23 views
4

我會先說我是Xcode/Swift中製作應用程序的非常新手。我從來沒有用Obj-C做過任何事情,所以Swift是我第一次進入iOS編碼領域。刪除核心數據中的記錄後,無法刪除索引處的表格行

我的問題是,我試圖刪除核心數據記錄後刪除表格單元格。我似乎能夠刪除Core數據,但每次嘗試刪除單元格行時都會得到相同的錯誤。

我得到的錯誤如下:

2016年3月10日08:49:56.484 EZ列表[31764:3225607] *在斷言失敗 - [UITableView的_endCellAnimationsWithContext:]/BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3512.30.14/UITableView.m:1720 2016-03-10 08:49:56.487 EZ列表[31764:3225607] *終止應用到期未捕獲異常'NSInternalInconsistencyException',原因:'無效更新:第0節中的行數無效。更新(2)後現有節中包含的行數必須等於該節之前包含的行數更新(2),加號或minu s從該部分插入或刪除的行數(0插入,1刪除)以及正數或負數移入或移出該部分的行數(移入0,移出0)。 [...等等]

而且我使用刪除代碼是這樣的:

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

    let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in 
     // delete item at indexPath 
     let request = self.fetchRequest() 
     var fetchResults = [AnyObject]() 

     do { 
      fetchResults = try self.moc.executeFetchRequest(request) 
     } catch { 
      fatalError("Fetching Data to Delete Failed") 
     } 

     self.moc.deleteObject(fetchResults[indexPath.row] as! NSManagedObject) 

     fetchResults.removeAtIndex(indexPath.row) 

     do { 
      try self.moc.save() 
     } catch { 
      fatalError("Failed to Save after Delete") 
     } 

     self.lists.removeAtIndex(indexPath.row) 
     self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 

    } 

    let edit = UITableViewRowAction(style: .Normal, title: "Edit") { (action, indexPath) in 
     // edit item at indexPath 

    } 

    edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) 

    return [delete, edit] 

} 

測試一堆我已驗證該問題是否與

self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 

但我不明白爲什麼它認爲部分行是無效的。任何幫助將不勝感激。到目前爲止,我所有的搜索都提出了Obj-C的答案或者很快的答案,這些答案似乎並沒有解決我的問題。

編輯: 添加代碼numberOfRowsInSection

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    // #warning Incomplete implementation, return the number of rows 
    if let sections = frc.sections { 
     let currentSection = sections[section] 
     return currentSection.numberOfObjects 
    } 

    return 0 
} 

EDIT2: 這裏是我的ViewController 全碼**請記住,我一直在嘗試一些不同的東西來得到它的工作所以有一些評論的東西在那裏。

import UIKit 
import CoreData 

class ListTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { 

let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext 
var frc: NSFetchedResultsController = NSFetchedResultsController() 
var lists : [Lists] = [] 

/* 
var listNames: [String] = ["Any List", "Other List"] 
var listItemCount: [Int] = [5, 6] 
var listIcons: [UIImage] = [UIImage(named: "Generic List")!, UIImage(named: "Generic List")!] 
*/ 

override func viewWillAppear(animated: Bool) { 

    let imageView = UIImageView(image: UIImage(named: "TableBackground")) 
    imageView.contentMode = .ScaleAspectFill 
    self.tableView.backgroundView = imageView 
    self.tableView.tableFooterView = UIView(frame: CGRectZero) 
    do { 
     self.lists = try moc.executeFetchRequest(fetchRequest()) as! [Lists] 
    } catch { 
     fatalError("Failed setting lists array to fetch request") 
    } 
    self.tableView.reloadData() 

} 

override func viewDidLoad() { 
    super.viewDidLoad() 

    checkAnyList() 

    frc = getFCR() 
    frc.delegate = self 

    do { 
     try frc.performFetch() 
    } catch { 
     fatalError("Failed to perform inital fetch") 
    } 

    self.tableView.reloadData() 

    // Uncomment the following line to preserve selection between presentations 
    //self.clearsSelectionOnViewWillAppear = true 

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller. 
    // self.navigationItem.rightBarButtonItem = self.editButtonItem() 
} 

override func viewDidAppear(animated: Bool) { 

    checkAnyList() 

    frc = getFCR() 
    frc.delegate = self 

    do { 
     try frc.performFetch() 
    } catch { 
     fatalError("Failed to perform inital fetch") 
    } 

    self.tableView.reloadData() 

} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 

// MARK: - Table view data source 

override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 
    // #warning Incomplete implementation, return the number of sections 
    if let sections = frc.sections { 
     return sections.count 
    } 

    return 0 
} 

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { 

    cell.backgroundColor = UIColor.clearColor() 

} 

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 
    // #warning Incomplete implementation, return the number of rows 
    if let sections = frc.sections { 
     let currentSection = sections[section] 
     return currentSection.numberOfObjects 
    } 

    return 0 
} 

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

    let listCellReturn = tableView.dequeueReusableCellWithIdentifier("listCell", forIndexPath: indexPath) as! ListTableViewCell 

    //let list = frc.objectAtIndexPath(indexPath) as! Lists 
    let list = self.lists[indexPath.row] 

    listCellReturn.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) 

    listCellReturn.listNameLabel.text = list.name 
    listCellReturn.listIconImage.image = UIImage(data: (list.image)!) 

    /* 
    listCellReturn.listNameLabel.text = listNames[indexPath.row] 
    listCellReturn.itemCountLabel.text = "Items: \(listItemCount[indexPath.row])" 
    listCellReturn.listIconImage.image = listIcons[indexPath.row] 
    */ 

    return listCellReturn 
} 

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

    let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in 
     // delete item at indexPath 
     let request = self.fetchRequest() 
     var fetchResults = [AnyObject]() 

     do { 
      fetchResults = try self.moc.executeFetchRequest(request) 
     } catch { 
      fatalError("Fetching Data to Delete Failed") 
     } 

     self.moc.deleteObject(fetchResults[indexPath.row] as! NSManagedObject) 

     fetchResults.removeAtIndex(indexPath.row) 

     do { 
      try self.moc.save() 
     } catch { 
      fatalError("Failed to Save after Delete") 
     } 

     self.lists.removeAtIndex(indexPath.row) 
     self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 

    } 

    let edit = UITableViewRowAction(style: .Normal, title: "Edit") { (action, indexPath) in 
     // edit item at indexPath 

    } 

    edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) 

    return [delete, edit] 

} 

func fetchRequest() -> NSFetchRequest { 

    let fetchRequest = NSFetchRequest(entityName: "Lists") 
    let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) 
    fetchRequest.sortDescriptors = [sortDescriptor] 

    return fetchRequest 

} 

func getFCR() -> NSFetchedResultsController { 

    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil) 

    return frc 

} 

func checkAnyList() { 

    var exists: Bool = false 

    let fetchReq = NSFetchRequest(entityName: "Lists") 
    let pred = NSPredicate(format: "%K == %@", "name", "Any List") 
    fetchReq.predicate = pred 

    do { 
     let check = try moc.executeFetchRequest(fetchReq) 
     for rec in check { 
      if let name = rec.valueForKey("name") { 
       if (name as! String == "Any List") { 
        exists = true 
       } 
      } 
     } 

     if (exists == false) { 
      let entityDesc = NSEntityDescription.entityForName("Lists", inManagedObjectContext: moc) 
      let list = Lists(entity: entityDesc!, insertIntoManagedObjectContext: moc) 

      list.name = "Any List" 
      list.image = UIImagePNGRepresentation(UIImage(named: "Any List")!) 

      do { 
       try moc.save() 
      } catch { 
       fatalError("New list save failed") 
      } 
     } 
    } catch { 
     fatalError("Error checking for Any List") 
    } 

     //let check = try moc.executeFetchRequest(fetchReq) 
     //for rec in check { 

     //} catch { 

} 

/* 
// Override to support conditional editing of the table view. 
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 
    // Return false if you do not want the specified item to be editable. 
    return true 
} 
*/ 

/* 
// Override to support editing the table view. 
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     // Delete the row from the data source 
     tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
    } else if editingStyle == .Insert { 
     // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view 
    }  
} 
*/ 

/* 
// Override to support rearranging the table view. 
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { 

} 
*/ 

/* 
// Override to support conditional rearranging of the table view. 
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool { 
    // Return false if you do not want the item to be re-orderable. 
    return true 
} 
*/ 

// MARK: - Navigation 

// In a storyboard-based application, you will often want to do a little preparation before navigation 
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 
    // Get the new view controller using segue.destinationViewController. 
    // Pass the selected object to the new view controller. 
} 

回答

4

你需要調用...

self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic) 

...當你的數據源已被更改。當你調用上述方法時,它會詢問你的tableView的委託,該部分有多少行,並且該方法仍然返回2。一旦知道該方法返回1,您必須調用上述方法。

編輯:獲取更多信息後更新。

查看NSFetchedResultsControllerDelegateReference)的參考資料。它詳細說明了你應該如何處理關於UITableView的數據變化。基本上從編輯方法中刪除deleteRowsAtIndexPaths,而是在適當的委託方法中執行tableView更新。沿着這些線應該幫助你(從上面的參考):

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject 
atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type 
newIndexPath:(NSIndexPath *)newIndexPath { 

    UITableView *tableView = self.tableView; 

    switch(type) { 

     case NSFetchedResultsChangeInsert: 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
        withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeDelete: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
        withRowAnimation:UITableViewRowAnimationFade]; 
      break; 

     case NSFetchedResultsChangeUpdate: 
      [self configureCell:[tableView cellForRowAtIndexPath:indexPath] 
       atIndexPath:indexPath]; 
      break; 

     case NSFetchedResultsChangeMove: 
      [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] 
        withRowAnimation:UITableViewRowAnimationFade]; 
      [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] 
        withRowAnimation:UITableViewRowAnimationFade]; 
      break; 
    } 
} 

你可能不需要處理每個案件。 NSFetchedResultsController參考也很有幫助(Reference)。

+0

請原諒,我不能完全理解。但我知道我確實會提到你提到的那條線,它看起來甚至是一樣的。但是我會在哪裏放置numberOfRows:inSection? –

+0

我已更新我的答案,以添加有關我詢問的方法的更多詳細信息。你有沒有實現這個方法:https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UITableViewDataSource_Protocol/#//apple_ref/occ/intfm/UITableViewDataSource/tableView:numberOfRowsInSection: – Mark

+0

哦,是的,我沒有添加到我原來的帖子。 –

0

我知道了!但這一切都要感謝馬克,他告訴我我需要做什麼。在Google上搜索了一些東西之後,我迅速找到了相應的東西。我只是想發佈我的最終代碼,以便任何需要最終結果的人也擁有它! 再次感謝你馬克,我花了一個星期在我的車輪上做這件事。

我感動self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic)internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)

裏面以下是完整的視圖控制器代碼

import UIKit 
import CoreData 

class ListTableViewController: UITableViewController, NSFetchedResultsControllerDelegate { 

let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext 
var frc: NSFetchedResultsController = NSFetchedResultsController() 

override func viewWillAppear(animated: Bool) { 

    let imageView = UIImageView(image: UIImage(named: "TableBackground")) 
    imageView.contentMode = .ScaleAspectFill 
    self.tableView.backgroundView = imageView 
    self.tableView.tableFooterView = UIView(frame: CGRectZero) 

} 

override func viewDidLoad() { 
    super.viewDidLoad() 

    checkAnyList() 

    frc = getFCR() 
    frc.delegate = self 

    do { 
     try frc.performFetch() 
    } catch { 
     fatalError("Failed to perform inital fetch") 
    } 

    // Uncomment the following line to preserve selection between presentations 
    //self.clearsSelectionOnViewWillAppear = true 

} 

override func viewDidAppear(animated: Bool) { 

    checkAnyList() 

    frc = getFCR() 
    frc.delegate = self 

    do { 
     try frc.performFetch() 
    } catch { 
     fatalError("Failed to perform inital fetch") 
    } 

    self.tableView.reloadData() 

} 

override func didReceiveMemoryWarning() { 
    super.didReceiveMemoryWarning() 
    // Dispose of any resources that can be recreated. 
} 

// MARK: - Table view data source 

override func numberOfSectionsInTableView(tableView: UITableView) -> Int { 

    if let sections = frc.sections { 
     return sections.count 
    } 

    return 0 
} 

override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { 

    cell.backgroundColor = UIColor.clearColor() 

} 

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 

    if let sections = frc.sections { 
     let currentSection = sections[section] 
     return currentSection.numberOfObjects 
    } 

    return 0 
} 

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

    let cell = tableView.dequeueReusableCellWithIdentifier("listCell", forIndexPath: indexPath) as! ListTableViewCell 

    let list = frc.objectAtIndexPath(indexPath) as! Lists 

    cell.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) 
    cell.listNameLabel.text = list.name 
    cell.listIconImage.image = UIImage(data: (list.image)!) 
    //listCellReturn.itemCountLabel.text = "Items: \(listItemCount[indexPath.row])" 

    return cell 
} 

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

    let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in 

     let request = self.fetchRequest() 
     var fetchResults = [AnyObject]() 

     do { 
      fetchResults = try self.moc.executeFetchRequest(request) 
     } catch { 
      fatalError("Fetching Data to Delete Failed") 
     } 

     self.moc.deleteObject(fetchResults[indexPath.row] as! NSManagedObject) 
     fetchResults.removeAtIndex(indexPath.row) 

     do { 
      try self.moc.save() 
     } catch { 
      fatalError("Failed to Save after Delete") 
     } 

    } 

    let edit = UITableViewRowAction(style: .Normal, title: "Edit") { (action, indexPath) in 

     self.performSegueWithIdentifier("newList", sender: tableView.cellForRowAtIndexPath(indexPath)) 

    } 

    edit.backgroundColor = UIColor.init(red: 84/255, green: 200/255, blue: 214/255, alpha: 1) 

    return [delete, edit] 

} 

internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { 

    if (type == NSFetchedResultsChangeType.Delete) { 
     self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) 
    } 

} 

func fetchRequest() -> NSFetchRequest { 

    let fetchRequest = NSFetchRequest(entityName: "Lists") 
    let sortDescriptor = NSSortDescriptor(key: "name", ascending: true) 
    fetchRequest.sortDescriptors = [sortDescriptor] 

    return fetchRequest 

} 

func getFCR() -> NSFetchedResultsController { 

    frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: nil, cacheName: nil) 

    return frc 

} 

func checkAnyList() { 

    var exists: Bool = false 

    let fetchReq = NSFetchRequest(entityName: "Lists") 
    let pred = NSPredicate(format: "%K == %@", "name", "Any List") 
    fetchReq.predicate = pred 

    do { 
     let check = try moc.executeFetchRequest(fetchReq) 
     for rec in check { 
      if let name = rec.valueForKey("name") { 
       if (name as! String == "Any List") { 
        exists = true 
       } 
      } 
     } 

     if (exists == false) { 
      let entityDesc = NSEntityDescription.entityForName("Lists", inManagedObjectContext: moc) 
      let list = Lists(entity: entityDesc!, insertIntoManagedObjectContext: moc) 

      list.name = "Any List" 
      list.image = UIImagePNGRepresentation(UIImage(named: "Any List")!) 

      do { 
       try moc.save() 
      } catch { 
       fatalError("New list save failed") 
      } 
     } 
    } catch { 
     fatalError("Error checking for Any List") 
    } 

} 

/* 
// Override to support conditional editing of the table view. 
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { 
    // Return false if you do not want the specified item to be editable. 
    return true 
} 
*/ 

/* 
// Override to support editing the table view. 
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { 
    if editingStyle == .Delete { 
     // Delete the row from the data source 
     tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) 
    } else if editingStyle == .Insert { 
     // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view 
    } 
} 
*/ 

/* 
// Override to support rearranging the table view. 
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { 

} 
*/ 

/* 
// Override to support conditional rearranging of the table view. 
override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool { 
    // Return false if you do not want the item to be re-orderable. 
    return true 
} 
*/ 

// MARK: - Navigation 

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { 

    if let _ = sender as? UITableViewCell { 
     let cell = sender as! UITableViewCell 
     let indexPath = tableView.indexPathForCell(cell) 

     let itemCont : AddListViewController = segue.destinationViewController as! AddListViewController 
     let list : Lists = self.frc.objectAtIndexPath(indexPath!) as! Lists 
     itemCont.list = list 
    } 

} 

}