Insert Row At End Of Section Error - ios

I'm trying to allow the user to insert rows at the end of sections but I'm getting an error. However everything seems correct so there must be something that I'm not seeing. Can someone point me in the right direction. Attached is my code, the error, and a screenshot of the table.
//
// Test.swift
// Table Views
//
// Created by Deion Long on 7/18/15.
// Copyright (c) 2015 Deion Long. All rights reserved.
//
import UIKit
var array = ["Section 1", "Section 2"]
extension UITableView {
func indexPathForView (view : UIView) -> NSIndexPath? {
let location = view.convertPoint(CGPointZero, toView:self)
return indexPathForRowAtPoint(location)
}
}
class Test: UIViewController, UITableViewDelegate, UITableViewDataSource {
var things:NSMutableArray = ["hi", "bye", "kie"]
#IBAction func editBtnClicked(sender: AnyObject) {
//When not in editing mode already set editing to true
if(table.editing == false){
self.editing = true
println("hi")
table.setEditing(true, animated: true)
}else{
self.editing = false
table.setEditing(false, animated: true)
}
}
#IBOutlet weak var table: UITableView!
#IBAction func doneButton(sender: AnyObject) {
table.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
table.allowsSelectionDuringEditing = true
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return array.count // This was put in mainly for my own unit testing
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return array[section]
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var addRow = self.editing ? 1 : 0
println(addRow)
return addRow + things.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("textInputCell3") as! DetailCell
if (indexPath.row >= things.count && self.editing) {
cell.configure(text: "Add Row", placeholder: "Enter")
} else{
cell.configure(text: things.objectAtIndex(indexPath.row) as! String, placeholder: "Enter")
}
return cell
}
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
self.table.setEditing(editing, animated: animated)
if(editing){
table.beginUpdates()
for var i = 0; i < self.table.numberOfSections(); i++
{
var lastRow = table.numberOfRowsInSection(i)
var lastIndex = NSIndexPath(forRow: lastRow, inSection: i)
table.insertRowsAtIndexPaths([lastIndex], withRowAnimation: UITableViewRowAnimation.Automatic)
}
table.endUpdates()
}
}
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
if(indexPath.row >= things.count){
return UITableViewCellEditingStyle.Insert
}else{
return UITableViewCellEditingStyle.Delete
}
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if (editingStyle == UITableViewCellEditingStyle.Delete) {
things.removeObject(indexPath.row)
table.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
}
else if (editingStyle == UITableViewCellEditingStyle.Insert) {
things.insertObject("123", atIndex: self.things.count)
table.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
}
}
ERROR: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'

The error message says it all. You mustn't insert the row until you've updated the model data to have the extra row. You may be confusing yourself with your sections here; practice with just one section first. Your model does not seem very well designed to distinguish the two sections.

In the setEditing: you are inserting rows inside the beginUpdates and endUpdates function of UITableView
table.beginUpdates()
for var i = 0; i < self.table.numberOfSections(); i++ {
var lastRow = table.numberOfRowsInSection(i)
var lastIndex = NSIndexPath(forRow: lastRow, inSection: i)
table.insertRowsAtIndexPaths([lastIndex], withRowAnimation: UITableViewRowAnimation.Automatic)
}
table.endUpdates()
When you are inserting rows make sure that you are updating your model object things. If you just want to reload the data of the cell use
table.reloadRowsAtIndexPaths([lastIndex], withRowAnimation: UITableViewRowAnimation.Automatic)

Related

Expand and collapse multilevel sections in uitableview swift4

I want to expand and collpase the multilevel array in uitableview like the following
Cat1
SubCat1
Info 1
Info 2
SubCat2
Info 1
Info 2
SubCat3
Info 1
Info 2
Cat2
SubCat1
Info 1
Info 2
For that purpose I have done the following code.
struct CellData {
var opened = Bool()
var subCatTitle = String()
var subCatList = [String]()
}
struct MainModel {
var opened = Bool()
var categoryTitle = String()
var categoryList = [CellData]()
}
I have made the list
#IBOutlet var expandableThreeStageTableView: UITableView!
var arrayList = [CellData]()
var expandableList = [MainModel]()
func loadData(){
arrayList.append(CellData(opened: false, subCatTitle: "SubCat1", subCatList: ["Info1","Info2","Info3"]))
arrayList.append(CellData(opened: false, subCatTitle: "SubCat2", subCatList: ["Info1","Info2","Info3"]))
arrayList.append(CellData(opened: false, subCatTitle: "SubCat3", subCatList: ["Info1","Info2"]))
arrayList.append(CellData(opened: false, subCatTitle: "SubCat4", subCatList: ["Info1"]))
expandableList.append(MainModel(opened: true, categoryTitle: "Cat1", categoryList: arrayList))
expandableList.append(MainModel(opened: false, categoryTitle: "Cat2", categoryList: arrayList))
expandableList.append(MainModel(opened: false, categoryTitle: "Cat3", categoryList: arrayList))
}
And delegate, datasource methods are given below
extension TextFieldAsSearchVC : UITableViewDataSource{
func numberOfSections(in tableView: UITableView) -> Int {
return expandableList.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section:
Int) -> Int {
if expandableList[section].opened{
if expandableList[section].categoryList[section].opened{
return
expandableList[section].categoryList[section].subCatList.count////which extra count should return here
}else{
print("COUNT ",expandableList[section].categoryList.count)
return expandableList[section].categoryList.count +
1///here +1 is for catname + subcatname
}
}else{
return 1
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath:
IndexPath) -> UITableViewCell {
if indexPath.row == 0{
let cell =
expandableThreeStageTableView.dequeueReusableCell(withIdentifier:
"TextFieldAsSearchVCCell", for: indexPath) as! TextFieldAsSearchVCCell
cell.lblValue.text =
expandableList[indexPath.section].categoryTitle
return cell
}else if indexPath.row <=
expandableList[indexPath.section].categoryList.count{
let cell =
expandableThreeStageTableView.dequeueReusableCell(withIdentifier:
"SectionDataCell", for: indexPath) as! SectionDataCell
cell.rowLabel.text =
expandableList[indexPath.section].categoryList[indexPath.row -
1].subCatTitle
return cell
}
else{
let cell =
expandableThreeStageTableView.dequeueReusableCell(withIdentifier:
"SectionDataCell", for: indexPath) as! SectionDataCell
cell.rowLabel.text =
expandableList[indexPath.section].categoryList[indexPath.row].
subCatList[indexPath.row]//how to access rows in subcategories
return cell
}
}
}
extension TextFieldAsSearchVC : UITableViewDelegate{
func tableView(_ tableView: UITableView, didSelectRowAt indexPath:
IndexPath) {
if indexPath.row == 0{
if expandableList[indexPath.section].opened{
expandableList[indexPath.section].opened = false
//now reload the section
let sections = IndexSet(integer: indexPath.section)
expandableThreeStageTableView.reloadSections(sections,
with: .automatic)
}else{
expandableList[indexPath.section].opened = true
//now reload sections
let sections = IndexSet(integer: indexPath.section)
expandableThreeStageTableView.reloadSections(sections,
with: .automatic)
}
}else {
if
expandableList[indexPath.section].categoryList[indexPath.row].opened{
expandableList[indexPath.section].categoryList[indexPath.row].opened =
false
expandableThreeStageTableView.reloadRows(at:
[IndexPath(index: indexPath.row)], with: .automatic)
}else{
expandableList[indexPath.section].categoryList[indexPath.row].opened =
true
expandableThreeStageTableView.reloadRows(at:
[IndexPath(index: indexPath.row)], with: .automatic)
}
}
}
}
From above code I can expand and collapse the Categories but not Subcategories.. When I tried to click on Subcategories it gives me an error
*** Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid index path for use
with UITableView. Index paths passed to table view must contain exactly
two indices specifying the section and row. Please use the category on
NSIndexPath in NSIndexPath+UIKitAdditions.h if possible.'
How to deal with such type of logic?
The specific error you are getting occurs in this line:
expandableThreeStageTableView.reloadRows(at:
[IndexPath(index: indexPath.row)], with: .automatic)
An IndexPath needs both, a row and a section; you're only providing a row. So it should be something like this:
expandableThreeStageTableView.reloadRows(at:
[IndexPath(row: indexPath.row, section: indexPath.section)], with: .automatic)
If you really only need to reload the current indexPath, simply call it like this:
expandableThreeStageTableView.reloadRows(at:
[indexPath], with: .automatic)
This would fix the error you are getting, but I don't know if that solves your problem or not.

UITableView cell insertion failure

I am trying to make a UITableView that can have expandable header views. When you press a button inside of the header view, the following function gets executed:
func expandTheCell(_ sender: UIButton) {
self.tableView.beginUpdates()
if sender.isExpanded == false {
postsArray.append("Hello")
tableView.reloadData()
print("Array Count: \(postsArray.count)")
self.tableView.insertRows(at: [IndexPath.init(row: 0, section: sender.tag)], with: .fade)
} else {
print("test")
}
self.tableView.endUpdates()
}
This are some table view functions:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postsArray.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
When I try to insert the rows, I get the following error:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 1
How come I can't insert the cells? What am I doing wrong?
I think problem is due to having same dataSource array for the sections, in your case postsArray , when you append the item to postsArray on clicking the button , same postsArray is used for other sections, so after you insert the row in section 0 , section 1 complains that number of rows for me before and after insert operation is not same, but section 0 doesnt complain because it has same number of rows and number of items in postsArray
Now this problem can be solved in two ways:
First way is that you can insert the row for other sections as well, then all the sections have equal number of rows as the number of elements in postsArray
Second way is that you have different dataSource arrays for all the sections , like postsArray1 for section 1, postsArray2 for section 2 and same for other sections. Now in this case you dont need to insert rows for other sections , since each section has different dataSource array, changing one wont affect others.
I have made a simple project to demonstrate the above theory:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(buttonTapped(_:)))
self.navigationItem.rightBarButtonItem = addButton
}
var valuesFirstSection = ["value1", "value2", "value3"]
var valuesSecondSection = ["value1Second", "value2Second", "value3Second"]
//if you want to have the same dataSource array then use this
//var sharedValues = ["value1Shared", "value2Shared", "value3Shared"] // shared dataSource array
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return valuesFirstSection.count
}else {
return valuesSecondSection.count
}
// //if you want to have the same dataSource array then
//use this
//return sharedValues.count;
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
if indexPath.section == 0 {
cell.textLabel?.text = valuesFirstSection[indexPath.row]
}else {
cell.textLabel?.text = valuesSecondSection[indexPath.row]
}
return cell
//if you want to have the same dataSource array then
//use this
//cell.textLabel?.text = sharedValues[indexPath.row]
//return cell
}
func buttonTapped(_ sender: UIBarButtonItem) {
//if you want to have the same dataSource array then
//you need to insert the rows for other sections as well
// sharedValues.insert("NewValue0", at: 0)
// self.tableView.insertRows(
// at: [IndexPath(row: 0, section: 0),
// IndexPath(row: 0, section: 1)
// ],
// with: .automatic)
valuesFirstSection.insert("NewValue0", at: 0)
self.tableView.insertRows(
at: [IndexPath(row: 0, section: 0)
],
with: .automatic)
}
}
Hope this helps.

App crush: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0

import UIKit
import MapKit
import CoreLocation
class CourseClass2: UIViewController, CLLocationManagerDelegate, MKMapViewDelegate, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak
var map: MKMapView!
#IBOutlet weak
var mapCourse: UIImageView!
#IBOutlet weak
var tableView: UITableView!
struct User {
var name: String
var images: UIImage
var coordinate: (Double, Double)
var type: String
var address: String
}
var rows = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
insertRowsMode3()
}
func insertRowsMode2() {
for i in 0.. < users.count {
insertRowMode2(ind: i, usr: users[i])
}
}
func insertRowMode2(ind: Int, usr: User) {
let indPath = IndexPath(row: ind, section: 0)
rows = ind + 1
tableView.insertRows(at: [indPath], with: .right)
}
func insertRowsMode3() {
rows = 0
insertRowMode3(ind: 0)
}
func insertRowMode3(ind: Int) {
let indPath = IndexPath(row: ind, section: 0)
rows = ind + 1
tableView.insertRows(at: [indPath], with: .right)
guard ind < users.count - 1
else {
return
}
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
self.insertRowMode3(ind: ind + 1)
}
}
func numberOfSections( in tableView: UITableView) - > Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) - > Int {
return rows
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",
for: indexPath) as!MyTableViewCell
let user = users[indexPath.row]
cell.MyImage.image = user.images
cell.MyLabel.text = user.name
cell.MyTypeLabel.text = user.type
return (cell)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "goToLast", sender: users[indexPath.row])
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) - > CGFloat {
return 100
}
override func prepare(
for segue: UIStoryboardSegue, sender: Any ? ) {
if segue.identifier == "goToLast" {
guard
let vc = segue.destination as ? FinalClass
else {
return
}
let guest = segue.destination as!FinalClass
if let user = sender as ? User {
guest.local = user.name
guest.localImage = user.images
guest.localType = user.type
guest.localAddress = user.address
}
}
}
#IBAction func IndTapped(_ sender: Any) {
self.performSegue(withIdentifier: "goBack", sender: self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Hello guys, this is the code of the ViewController where i get the error, i added a tableView with a particular animation but when i change the view and than come back with the dismiss(animated: true, completion: nil)the application crash because there is some inconsistency with rows data. As you can see, when i'm first visiting this CourseClass2 controller i'm setting up row by calling insertRowsMode3 in viewDidAppear.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
insertRowsMode3()
}
The problem is when i come back the row data is not same as i have initialised it with zero and viewDidAppear doesn't get called.
I know the error but i don't how i can change my code to make it work. I really need an help.
The app crush exactly in this func
func insertRowMode3(ind:Int) {
let indPath = IndexPath(row: ind, section: 0)
rows = ind + 1
tableView.insertRows(at: [indPath], with: .right)
guard ind < users.count-1 else { return }
DispatchQueue.main.asyncAfter(deadline: .now()+0.15) {
self.insertRowMode3(ind: ind+1)
}
}
here tableView.insertRows(at: [indPath], with: .right)
You mentioned invalid no of rows in section 0 :
In insertRowMode3 , you are inserting rows but not updating the table view.
So your table view getting the same count of no of rows in table view even afer updating, thats where inconsistency is.
Use tableview.beginUpdates() ,
then insert rows ,
increment ur rows value ,
then tableview.endUpdates()
Let me know if it doesn't help you out.

Programmatically moving UITableViewCell fails if manually moved first

I have a UITableView containing custom cells. These cells are able to be manually moved. In addition, a switch on the cell causes it to be moved to the bottom. The switch works fine if it is used separately from drag-to-reorder. However, if I drag-to-reorder first, then use a switch, I get the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
The controller has the following code relating to the table:
//Table Delegate/Datasource
func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
let object = self.eventTypes[sourceIndexPath.row]
self.eventTypes.insertObject(object, atIndex: sourceIndexPath.row)
self.eventTypes.removeObject(sourceIndexPath.row)
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, shouldIndentWhileEditingRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.None
}
func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:S360SEventTypeTableCell? = tableView.dequeueReusableCellWithIdentifier(XIBFiles.EVENTTYPETABLECELL) as? S360SEventTypeTableCell
if ((cell == nil)){
tableView.registerNib(UINib(nibName: XIBFiles.EVENTTYPETABLECELL, bundle: nil), forCellReuseIdentifier: XIBFiles.EVENTTYPETABLECELL)
cell = tableView.dequeueReusableCellWithIdentifier(XIBFiles.EVENTTYPETABLECELL) as? S360SEventTypeTableCell
}
let eventType = eventTypes[indexPath.row]
cell!.iconImg.image = Images.get_event_image(eventType["title"]! as! String)
cell!.titleLbl.text = (eventType["title"]! as! String)
return cell!
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventTypes.count
}
The custom cell has the following code:
#IBOutlet var iconImg:UIImageView!
#IBOutlet var titleLbl:UILabel!
#IBOutlet var timeField:UITextField!
#IBOutlet var activeSwtch:UISwitch!
#IBOutlet var durationLbl:UILabel!
#IBOutlet var minsLbl:UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
override func layoutSubviews() {
super.layoutSubviews()
//Styling
timeField.layer.borderColor = Colors.REALLIGHTGREY.CGColor
timeField.layer.borderWidth = Numbers.BORDERREG
timeField.layer.cornerRadius = Numbers.CORNERRADIUS
timeField.tintColor = Colors.REDNESS
activeSwtch.onTintColor = Colors.REDNESS
self.showsReorderControl = true
}
func getTableView() -> UITableView?{
var tableView:UITableView? = nil
var view:UIView = self
while !view.isKindOfClass(UITableView.self) && view.superview != nil {
view = view.superview!
}
if view.isKindOfClass(UITableView.self) {
tableView = (view as! UITableView)
}
return tableView
}
#IBAction func activeSwtchTouch(){
if self.activeSwtch.on{
self.titleLbl.enabled = true
self.timeField.enabled = true
self.durationLbl.enabled = true
self.minsLbl.enabled = true
self.iconImg.alpha = 1.0
self.showsReorderControl = true
}
else{
let tableView = self.getTableView()
if tableView != nil {
let fromPath = tableView!.indexPathForCell(self)!
let toPath = NSIndexPath(forRow: tableView!.numberOfRowsInSection(fromPath.section) - 1, inSection: fromPath.section)
print("FROM: " + String(fromPath.row) + ":" + String(fromPath.section))
print("TO: " + String(toPath.row) + ":" + String(toPath.section))
print("SECTIONS: " + String(tableView!.numberOfSections))
print("ROWS: " + String(tableView!.numberOfRowsInSection(fromPath.section)))
tableView!.moveRowAtIndexPath(fromPath, toIndexPath: toPath)
self.titleLbl.enabled = false
self.timeField.enabled = false
self.durationLbl.enabled = false
self.minsLbl.enabled = false
self.iconImg.alpha = 0.5
self.showsReorderControl = false
}
}
}
Of Note: The custom delegate method for moveRowAtIndexPath seems to only be called when drag-to-reorder is used, but not when the UISwitch touch triggers. At the very least, the breakpoints in that method do not get hit when UISwitch touch trigger is used.
Dumb mistake. This line self.eventTypes.removeObject(sourceIndexPath.row) should be self.eventTypes.removeObject(object). The object was never being removed, and therefore, when a reload or update finishing was being called, it was not able to determine properly the amount of items, since it was growing with each move.

swipe to delete UITableView cell animation is not working in swift 2

The swipe delete functionality is working fine, but i'm not able to add the animation, i tried everything i can but nothing worked. Whenever I add the code for animation, the app crashes when the cell is deleted. If you load the application again, you will find that the record was deleted, implying that the deletion was successful.
The crash error i'm getting is:
Invalid update: invalid number of rows in section 0. The number of
rows contained in an existing section after the update (1) must be
equal to the number of rows contained in that section before the
update (1), plus or minus the number of rows inserted or deleted from
that section (0 inserted, 1 deleted) and plus or minus the number of
rows moved into or out of that section (0 moved in, 0 moved out).
The animation code blocks i tried were:
//1
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
//2
tableView.deleteRowsAtIndexPaths([indexPath],
withRowAnimation: UITableViewRowAnimation.Automatic)
I also tried to remove those codes and it didn't work too:
fetchAndSetResults()
treatmentProtocolsTableView.reloadData()
The entire code in the swift file is here, I commented out the animation blocks, and it works properly.
import UIKit
import CoreData
class Tx_Protocols: UIViewController, UITableViewDataSource, UITableViewDelegate {
//MARK: declaratoins.
weak var secureTextAlertAction: UIAlertAction?
//MARK: Core data related
var txProtocols = [TreatmentProtocolData]()
var selectedProtocol : TreatmentProtocolData? = nil
#IBOutlet weak var treatmentProtocolsTableView: UITableView!
//When button + is clicked, segue show add tx VC is initiated.
#IBAction func plusButtonAddTxProtocol(sender: AnyObject) {
self.performSegueWithIdentifier("showAddTxVC", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
fetchAndSetResults()
treatmentProtocolsTableView.delegate = self
treatmentProtocolsTableView.dataSource = self
}
//When this is used, the data shows with a slight lag, slower.
override func viewDidAppear(animated: Bool) {
fetchAndSetResults()
treatmentProtocolsTableView.reloadData()
}
//This is how you catch the app delegate core data fnxnality, GRABBING THE PROPERTY IN APP DELEGATE
func fetchAndSetResults() {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "TreatmentProtocolData")
do {
let results = try context.executeFetchRequest(fetchRequest)
self.txProtocols = results as! [TreatmentProtocolData]
} catch let err as NSError {
print(err.debugDescription)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier:"Cell")
cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
cell.textLabel!.adjustsFontSizeToFitWidth = true
cell.textLabel!.font = UIFont.boldSystemFontOfSize(17)
let treatmentProtocol = txProtocols[indexPath.row]
cell.textLabel!.text = treatmentProtocol.title
cell.imageView?.image = treatmentProtocol.getTxProtocolImage()
return cell
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return txProtocols.count
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.selectedProtocol = txProtocols[indexPath.row]
self.performSegueWithIdentifier("showTxProtocol", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTxProtocol" {
let detailVC = segue.destinationViewController as! ShowTxProtocolDetailVC
detailVC.txProtocol = self.selectedProtocol
}
}
//MARK: Edittable table, delete button functionality.
func tableView(tableView: UITableView,
canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func tableView(tableView: UITableView,
commitEditingStyle
editingStyle: UITableViewCellEditingStyle,
forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext
//1
let protocolToRemove =
txProtocols[indexPath.row]
//2
context.deleteObject(protocolToRemove)
//3
do {
try context.save()
} catch let error as NSError {
print("Could not save: \(error)")
}
// //1
// tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
// //2
// tableView.deleteRowsAtIndexPaths([indexPath],
// withRowAnimation: UITableViewRowAnimation.Automatic)
fetchAndSetResults()
treatmentProtocolsTableView.reloadData()
}
}
}
I appreciate your help
You need to encapsulate inside beginUpdates() and endUpdates(). Also, update your data model that is used to load the table view:
self.tableView.beginUpdates()
self.txProtocols.removeObjectAtIndex(indexPath.row) // Check this out
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
self.tableView.endUpdates()
Based on the accepted answer by Abhinav, here was my solution (Swift 3). This implemented in my NSFetchedResultsControllerDelegate:
func controllerWillChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.tableView.beginUpdates()
}
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {
...
case .delete:
// Delete from tableView
removeFromSetsToDisplayByID(removeThisSet: myObject)
tableView.deleteRows(at: [indexPath!], with: UITableViewRowAnimation.automatic)
...
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.tableView.endUpdates()
}

Resources