0

I think this is probably an X Y problem, so I'll give some background info first.

I am building an app that can show a "form". The user can fill in the form with some stuff and create some custom things.

I think the most suitable thing to do is to use a table view for the form. And I can display all the text boxes that need to be fill in, in each of the table cells.

Here's a screenshot:

enter image description here

The "Add New Input" button will insert a new cell on the bottom when it is tapped. And if you swipe one of the inputs to the left, you get a "Delete" button. You can use that to delete the input.

As you can see, this table view needs to add and delete rows.

Originally, I was using a cocoapod called "TableViewModel" which makes this super easy. But then I found a really severe bug in the library so I don't want to use it anymore.

I tried using the table view's deleteRowsAtIndexPaths and insertRowsAtIndexPaths methods. But if I do it like this:

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

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

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = UITableViewCell()
    cell.textLabel?.text = "Hello"
    return cell
}

// I use motionEnded here because I don't want to add a button or anything just to test this
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent?) {
    tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: 0, inSection: 0)], withRowAnimation: .Fade)
}

It will result in an exception saying that the table view is inconsistent with the data source after I deleted one row. This means I have to have code to handle this inconsistency. This will definitely make my code harder to read.

The same thing goes with the insert method.

Also, I need to keep track of how many rows there are in each section. And my code just becomes really messy and unmaintainable with all that.

I have also searched for other libraries but they are all really weird and not as straightforward as "tableViewModel". They all seem to require me to create a model for the table view. I don't understand how to do that in my case. I just want to display a bunch of text fields!

How can I insert or delete rows more elegantly? I think I either need to write an extension of the table view or learn to write a model for my table view. But I am able to do neither of these methods.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • because you don't have any data source. Also see logically: suppose you add a new row with insertRowsAtIndexPaths . So now you have 2 rows. However your numberOfRowsInSection always returning 1. so it will crash and vice versa for delete. Table view are supposed to work with a collection (NSArray, NSDictionary, NSSet etc.). – maddy May 17 '16 at 14:27
  • The thing, is that when you add or remove a row, you always say that there is only 1 row in the section. You have to use a Array or a Dictionary, to keep track of how many data (and which data) in each row and each section, and return that value in `numberOfSectionsInTableView:` and `tableView:`numberOfRowsInSection:`. – Larme May 17 '16 at 14:28
  • You mean I should not do this kind of form using a table view? Then what can I do it with? @Alok – Sweeper May 17 '16 at 14:28
  • @Larme Yes I know. That's why I said it's not elegant. I need to add so much boilerplate. And it kind of makes the code harder to read and maintain. I am asking how to do it in a way such that my code would be easier to read. – Sweeper May 17 '16 at 14:30

3 Answers3

0

Because you don't have any data source. Also see logically: suppose you add a new row with insertRowsAtIndexPaths . So now you have 2 rows. However your numberOfRowsInSection always returning 1. so it will crash and vice versa for delete. Table view are supposed to work with a collection (NSArray, NSDictionary, NSSet etc.). For your help:

Already they have nade a form as yours

obj-c easy to understand how table view with data source work

Adding, Updating, Deleting and Moving records using Swift.

maddy
  • 4,001
  • 8
  • 42
  • 65
0

You may use a TableView to create the form but you must design an appropriate dataSource to keep track of these data. Now the dataSource method must be modified to

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return yourDataSourceForSection.count
}

You may edit this dataSource when a delete operation is performed as follows

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
    if editingStyle == .Delete {
        //Delete the row from the data source to ensure consistency
        self.array.removeAtIndex(indexPath.row)
        tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
    }
}

You may use a model class as the dataSource.

class NewCustomOperation: NSObject {
   var name: String?
   var rejectFloatingPoints: Bool?
   var inputs: AvailableInputs?
   var results: Results?
}

class AvailableInputs: NSObject {
   var name: [String]?
   var description: [String]?
}

class Results: NSObject {
   var name: [String]?
   var formula: [String]?
}
Mathews
  • 733
  • 5
  • 11
  • Yes I know that, but what would the datasource be if I have a bunch of textfields for the user to fill in, instead of a bunch of data to display? – Sweeper May 17 '16 at 14:38
  • Have you considered using stack views? Maybe a lot of work, but they would manage your interface for you super smooth. – user3069232 May 17 '16 at 19:29
  • @user3069232 but I am using iOS8 though, I don't think stack views are available. – Sweeper May 17 '16 at 23:23
  • Don't know what the plan is or requirement, but with IOS 10.0 just around the corner, you should upgrade to at least IOS 9.x, what's keeping you @ IOS 8.0 ? – user3069232 May 18 '16 at 04:00
  • I have added a model class as an example for your data source – Mathews May 19 '16 at 08:01
0

I found a solution!

I basically keep an array of an array of cells for the table view to display:

var cells: [[UITableViewCell]] = [[], [], []] // I have 3 sections, so 3 empty arrays

And then I added these data source methods:

override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return cells.count
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return cells[section].count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    return cells[indexPath.section][indexPath.row]
}

Now, I can add and remove cells super easily:

func addCellToSection(section: Int, index: Int, cell: UITableViewCell) {
    cells[section].insert(cell, atIndex: index)
    tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: section)], withRowAnimation: .Left)
}

func removeCellFromSection(section: Int, index: Int) {
    cells[section].removeAtIndex(index)
    tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: section)], withRowAnimation: .Left)
}

With just two lines, I can add/remove cells with animation!

Sweeper
  • 213,210
  • 22
  • 193
  • 313