Reputation: 348
I am working on an app that will include 13 forms that can be filled in and send of to a web service. All these forms include multiple fields, although several of the fields are included on more than one form.
Of course I could have taken the route of simply creating 13 view controllers. The forms will not change in the near future, so static cells could do the trick but creating 13 view controllers in code and in the storyboard does not seem elegant.
So I have decided to create one view controller with a variety of dynamic prototype cells which then can be used by the different types of forms when required.
I created a simple model, and use the selected FormType to see which fields (tableview cells) are needed in that form.
struct Form {
let type: FormType
let identifier: String
let cellTypes: [CellType]
let requiredFieldsIndexes: [Int]
}
enum FormType: String {
case permissionEndPoint = "FAP-11"
case other2 = "2"
// etc...
}
Then I create the 13 forms in code:
Form(type: .permissionEndPoint, identifier: "SAP.01", cellTypes: Array([.kilometerConstraints,.additionalInformation,.permissionNumber]),requiredFieldsIndexes: [0])
And use the tableview delegate methods to show the correct fields.` func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return displayedForm.cellTypes.count }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = displayedForm.cellTypes[indexPath.row]
switch identifier {
case .trafficControlCenter:
return tableView.dequeueReusableCell(withIdentifier: identifier.rawValue, for: indexPath)
case .trainInformation:
return tableView.dequeueReusableCell(withIdentifier: identifier.rawValue, for: indexPath)
case .permissionAtLocation:
return tableView.dequeueReusableCell(withIdentifier: identifier.rawValue, for: indexPath)
case .notUsed:
let cell = tableView.dequeueReusableCell(withIdentifier: identifier.rawValue, for: indexPath) as! NotUsedTableViewCell
return cell
case .additionalInformation:
let cell = tableView.dequeueReusableCell(withIdentifier: identifier.rawValue, for: indexPath) as! AdditionalInformationCell
cell.additionalInfoTextView.delegate = self
return cell
default:
return tableView.dequeueReusableCell(withIdentifier: identifier.rawValue, for: indexPath)
}
}`
This works well, but I need to check if certain if certain fields are filled out, collect the data, and send the data in a certain format to the web. How should I get access to the different fields now that I don't know exactly which fields are shown? Perhaps I could create a reference to all relevant IBOutlets (textfields, textviews and datepickers) to check these?
Is my general setup alright or is there an easier way of modelling this issue?
Any help is very appreciated.
Upvotes: 1
Views: 131
Reputation: 412
There are different moving parts in your solution and the important thing is to keep concerns separated between the different classes.
Following the MVC pattern, this is how I would do it:
UITableViewCell
, which has all the necessary outlets to controls in the cell (text fields, sliders, switches, etc).UITextFieldDelegate
) or the receiver of the actions of controls like UISwitch
, to detect when the user changes the values in the form.tableView(_:cellForRowAt:)
method you show above.tableView(_:cellForRowAt:)
method gets called to dequeue a cell, you repopulates the cell with the data you stored in the data source. As I said, cells can be reused, so you could get a reused cell with other data in it, or a newly dequeued and empty one. This is why you need to pass data back to it. If you don't the user will see weird values in the form if he scrolls up and down.Upvotes: 1