新表格视图单元格显示为空白单元格
我一直在做一个应用程序,遇到了一些问题,所以我重build了它尽可能简单,以find错误发生的地方。 我的代码使用CoreData,我的第一个例子有在导航栏的加号button中添加新项目的代码。 换句话说,没有改变观点或继续。 这个版本正如我所期望的那样工作。 看到下面的GIF。
但是,当我做一个新的视图控制器添加新的项目,我最终得到一个空白单元格。 我在加号button后面有一个完全相同的代码。 而我只是使用show segue从tableview中获取一个普通的视图控制器。 但是,如果我退出应用程序,并启动备份,这一切都显示正确。 见下文。
如果你好奇的话,那么我将把它添加到1到3之间的一个随机数中。我的猜测是我需要对这个segue进行一些处理,但是我不知道它是什么。 我在Swift上很新,所以我有很多东西要学。
提前感谢您的帮助,您可以给我。 我想如果我能解决这个问题,我终于可以开发我的应用程序了。 再次感谢你。 下面是我的代码,它有一个segue的情况。
表视图控制器代码(Swift)
import UIKit import CoreData class ListItemsTVC: UITableViewController, NSFetchedResultsControllerDelegate { // MARK: - Constants and Variables let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext var frc: NSFetchedResultsController = NSFetchedResultsController() // MARK: - App loading Functions override func viewDidLoad() { super.viewDidLoad() frc = getFCR() frc.delegate = self do { try frc.performFetch() } catch { print("Failed to perform inital fetch") } self.tableView.rowHeight = 62 } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Outlets and Actions /*@IBAction func addItem(sender: AnyObject) { let entityDesc = NSEntityDescription.entityForName("Items", inManagedObjectContext: moc) let item = Items(entity: entityDesc!, insertIntoManagedObjectContext: moc) if (NSUserDefaults.standardUserDefaults().objectForKey("nextItemID") == nil) { NSUserDefaults.standardUserDefaults().setObject(1, forKey: "nextItemID") NSUserDefaults.standardUserDefaults().synchronize() } let id = NSUserDefaults.standardUserDefaults().integerForKey("nextItemID") item.id = id switch id { case 1...9: item.name = "Item ID: 0\(id)" default: item.name = "Item ID: \(id)" } item.brand = "Brand \(id)" item.qty = 1 item.price = 0 item.size = "Size \(id)" let sec: Int = Int(arc4random_uniform(UInt32(4 - 1))) + 1 item.section = "Section \(sec)" item.isChecked = false do { try moc.save() NSUserDefaults.standardUserDefaults().setObject(id + 1, forKey: "nextItemID") NSUserDefaults.standardUserDefaults().synchronize() } catch { fatalError("New item save failed") } }*/ // 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, numberOfRowsInSection section: Int) -> Int { if let sections = frc.sections { let currentSection = sections[section] return currentSection.numberOfObjects } return 0 } override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 28 } override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if let sections = frc.sections { let currentSection = sections[section] return currentSection.name } return nil } func controllerWillChangeContent(controller: NSFetchedResultsController) { tableView.beginUpdates() } func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("listContentCell", forIndexPath: indexPath) as! ListItemsTVCell let item = frc.objectAtIndexPath(indexPath) as! Items cell.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) cell.itemName.text = item.name cell.itemSection.text = item.section cell.itemQty.text = "Qty: \(item.qty!)" cell.itemSize.text = item.size cell.itemPrice.text = floatToCurrency(Float(item.price!)) //cell.itemImage.image = UIImage(data: item.image!) cell.itemID.text = String(item.id!) return cell } override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in let item = self.frc.objectAtIndexPath(indexPath) as! Items let id = item.id! let request = self.fetchRequest() let pred = NSPredicate(format: "%K == %@", "id",id) request.predicate = pred var fetchResults = [AnyObject]() do { fetchResults = try self.moc.executeFetchRequest(request) } catch { fatalError("Fetching Data to Delete Failed") } self.moc.deleteObject(fetchResults[0] as! NSManagedObject) fetchResults.removeAtIndex(0) do { try self.moc.save() } catch { fatalError("Failed to Save after Delete") } } return [delete] } func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { switch type { case NSFetchedResultsChangeType.Delete: self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) case NSFetchedResultsChangeType.Insert: self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) /*case NSFetchedResultsChangeType.Update: tableView.reloadSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)*/ default: print("Default in didChangeSection was called") break } } internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case NSFetchedResultsChangeType.Delete: self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) case NSFetchedResultsChangeType.Insert: self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade) default: print("Default in didChangeObject was called") break } } /* // 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. } */ // MARK: - Custom Functions func fetchRequest() -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Items") let sortDesc1 = NSSortDescriptor(key: "section", ascending: true) let sortDesc2 = NSSortDescriptor(key: "isChecked", ascending: true) let sortDesc3 = NSSortDescriptor(key: "name", ascending: true) fetchRequest.sortDescriptors = [sortDesc1, sortDesc2, sortDesc3] return fetchRequest } func getFCR() -> NSFetchedResultsController { frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "section" , cacheName: nil) return frc } func floatToCurrency(flt: Float) -> String { let formatter = NSNumberFormatter() formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle return String(formatter.stringFromNumber(flt)!) } }
视图控制器(Swift)[Segue视图]
import UIKit import CoreData class AddItemListVC: UIViewController, NSFetchedResultsControllerDelegate { // MARK: - Constants and Variables let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext //var frc: NSFetchedResultsController = NSFetchedResultsController() // MARK: - App loading Functions override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Outlets and Actions @IBAction func addItem(sender: AnyObject) { let entityDesc = NSEntityDescription.entityForName("Items", inManagedObjectContext: moc) let item = Items(entity: entityDesc!, insertIntoManagedObjectContext: moc) if (NSUserDefaults.standardUserDefaults().objectForKey("nextItemID") == nil) { NSUserDefaults.standardUserDefaults().setObject(1, forKey: "nextItemID") NSUserDefaults.standardUserDefaults().synchronize() } let id = NSUserDefaults.standardUserDefaults().integerForKey("nextItemID") item.id = id switch id { case 1...9: item.name = "Item ID: 0\(id)" default: item.name = "Item ID: \(id)" } item.brand = "Brand \(id)" item.qty = 1 item.price = 0 item.size = "Size \(id)" let sec: Int = Int(arc4random_uniform(UInt32(4 - 1))) + 1 item.section = "Section \(sec)" item.isChecked = false do { try moc.save() NSUserDefaults.standardUserDefaults().setObject(id + 1, forKey: "nextItemID") NSUserDefaults.standardUserDefaults().synchronize() } catch { fatalError("New item save failed") } } /* // 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. } */ }
重新加载TableView数据是viewWillAppear
方法如下:
override func viewWillAppear() { super.viewWillAppear() do { try frc.performFetch() } catch { print("Failed to perform inital fetch") } }
当你从viewController
返回导航的时候,你并没有告诉TableViewController
更新tableView,所以在viewWillAppear中这样做,因为当你popupnavController返回的时候会触发它。
这是为我解决问题的代码。 我只改变了表格视图控制器代码:
import UIKit import CoreData class ListItemsTVC: UITableViewController, NSFetchedResultsControllerDelegate { // MARK: - Constants and Variables let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext var frc: NSFetchedResultsController = NSFetchedResultsController() // MARK: - App loading Functions override func viewDidLoad() { super.viewDidLoad() frc = getFCR() frc.delegate = self do { try frc.performFetch() } catch { print("Failed to perform inital fetch") } self.tableView.rowHeight = 62 } override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) self.tableView.reloadData() } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } // MARK: - Outlets and Actions /*@IBAction func addItem(sender: AnyObject) { let entityDesc = NSEntityDescription.entityForName("Items", inManagedObjectContext: moc) let item = Items(entity: entityDesc!, insertIntoManagedObjectContext: moc) if (NSUserDefaults.standardUserDefaults().objectForKey("nextItemID") == nil) { NSUserDefaults.standardUserDefaults().setObject(1, forKey: "nextItemID") NSUserDefaults.standardUserDefaults().synchronize() } let id = NSUserDefaults.standardUserDefaults().integerForKey("nextItemID") item.id = id switch id { case 1...9: item.name = "Item ID: 0\(id)" default: item.name = "Item ID: \(id)" } item.brand = "Brand \(id)" item.qty = 1 item.price = 0 item.size = "Size \(id)" let sec: Int = Int(arc4random_uniform(UInt32(4 - 1))) + 1 item.section = "Section \(sec)" item.isChecked = false do { try moc.save() NSUserDefaults.standardUserDefaults().setObject(id + 1, forKey: "nextItemID") NSUserDefaults.standardUserDefaults().synchronize() } catch { fatalError("New item save failed") } }*/ // 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, numberOfRowsInSection section: Int) -> Int { if let sections = frc.sections { let currentSection = sections[section] return currentSection.numberOfObjects } return 0 } override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 28 } override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { if let sections = frc.sections { let currentSection = sections[section] return currentSection.name } return nil } func controllerWillChangeContent(controller: NSFetchedResultsController) { tableView.beginUpdates() } func controllerDidChangeContent(controller: NSFetchedResultsController) { tableView.endUpdates() } override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCellWithIdentifier("listContentCell", forIndexPath: indexPath) as! ListItemsTVCell let item = frc.objectAtIndexPath(indexPath) as! Items cell.separatorInset = UIEdgeInsets(top: 0, left: 78, bottom: 0, right: 0) cell.itemName.text = item.name cell.itemSection.text = item.section cell.itemQty.text = "Qty: \(item.qty!)" cell.itemSize.text = item.size cell.itemPrice.text = floatToCurrency(Float(item.price!)) //cell.itemImage.image = UIImage(data: item.image!) cell.itemID.text = String(item.id!) return cell } override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? { let delete = UITableViewRowAction(style: .Destructive, title: "Delete") { (action, indexPath) in let item = self.frc.objectAtIndexPath(indexPath) as! Items let id = item.id! let request = self.fetchRequest() let pred = NSPredicate(format: "%K == %@", "id",id) request.predicate = pred var fetchResults = [AnyObject]() do { fetchResults = try self.moc.executeFetchRequest(request) } catch { fatalError("Fetching Data to Delete Failed") } self.moc.deleteObject(fetchResults[0] as! NSManagedObject) fetchResults.removeAtIndex(0) do { try self.moc.save() } catch { fatalError("Failed to Save after Delete") } } return [delete] } func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { switch type { case NSFetchedResultsChangeType.Delete: self.tableView.deleteSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) case NSFetchedResultsChangeType.Insert: self.tableView.insertSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic) /*case NSFetchedResultsChangeType.Update: tableView.reloadSections(NSIndexSet(index: sectionIndex), withRowAnimation: UITableViewRowAnimation.Automatic)*/ default: print("Default in didChangeSection was called") break } } internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { switch type { case NSFetchedResultsChangeType.Delete: self.tableView.deleteRowsAtIndexPaths([indexPath!], withRowAnimation: UITableViewRowAnimation.Automatic) case NSFetchedResultsChangeType.Insert: self.tableView.insertRowsAtIndexPaths([newIndexPath!], withRowAnimation: UITableViewRowAnimation.Fade) default: print("Default in didChangeObject was called") break } } /* // 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. } */ // MARK: - Custom Functions func fetchRequest() -> NSFetchRequest { let fetchRequest = NSFetchRequest(entityName: "Items") let sortDesc1 = NSSortDescriptor(key: "section", ascending: true) let sortDesc2 = NSSortDescriptor(key: "isChecked", ascending: true) let sortDesc3 = NSSortDescriptor(key: "name", ascending: true) fetchRequest.sortDescriptors = [sortDesc1, sortDesc2, sortDesc3] return fetchRequest } func getFCR() -> NSFetchedResultsController { frc = NSFetchedResultsController(fetchRequest: fetchRequest(), managedObjectContext: moc, sectionNameKeyPath: "section" , cacheName: nil) return frc } func floatToCurrency(flt: Float) -> String { let formatter = NSNumberFormatter() formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle return String(formatter.stringFromNumber(flt)!) } }
穆罕默德的回答肯定会解决这个问题,因为每次viewWillAppear会调用它将触发重新载入表视图。 但问题是,这不是一个很好的做法。
你看, viewWillAppear
会被调用很多次。 这是一个UIViewController生命周期方法。 如果你要去第二个视图控制器,并没有按下添加button,它仍然会重新加载UITableView。 现在,重点不在于是否是一个昂贵的操作,重点是这个viewWillAppear
不是做这种东西的地方。 如果在重新加载单元格之前必须做其他事情,那么可能不希望将其全部放在那里,并有可能造成错误。
实现这一目标的一个好方法是通过代表(回叫)。 阅读更多关于这个,你会看到它给你一个非常需要的灵活性。