I have a UITableView with 2 sections.
Each section has different xib cells (with multiple textfields, radio buttons etc.).
The user can enter those values using the keyboard.
How can I get the value of each text field at submit button using model?
Here is my model class for cell for detail section:
class DataModel {
var name: String?
var gender: String?
init(name: String?, gender: String?)
{
self.name = name
self.gender = gender
}
}
How I use it in my tableview:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier:"DetailCell") as? DetailCell
if cell == nil{
let arrNib:Array = Bundle.main.loadNibNamed("DetailCell",owner: self, options: nil)!
cell = arrNib.first as? DetailCell
}
let model: DataModel
model = DataModel [indexPath.row]
//displaying values
cell?.name_lbl.text = model.name
cell?.gender_lbl.text = model.gender
return cell!
}
First, you name_lbl and gender_lbl shoule be UITextField, not the UILabel.
You have to add target for the name_lbl and gender_lbl:
cell?.name_lbl.text = model.name
cell?.name_lbl.tag = indexPath.section
cell?.name_lbl.addTarget(self, action: #selector(textFieldNameDidChange(_:)), for: UIControl.Event.editingChanged)
cell?.gender_lbl.text = model.gender
cell?.name_lbl.tag = indexPath.section
cell?.name_lbl.addTarget(self, action: #selector(textFieldGenderDidChange(_:)), for: UIControl.Event.editingChanged)
and you add methods textFieldNameDidChange and textFieldGenderDidChange as:
#objc func textFieldGenderDidChange(sender: UITextField) {
var model = DataModel[sender.tag]
model.gender = sender.text
}
You should do the same for the "name".
You can give tag to textfield and use UITextFieldDelegate for this like,
func textFieldDidEndEditing(_ textField: UITextField) {
if textField.tag == 0 {
model.name = textField.text ?? ""
}
}
on you button submit, you can access model value like
#IBAction func submitButtonTapped(_ sender: Any) {
self.view.endEditing(true)
let name = model.name
}
For this u need to set tag to textfield in cell for row. In shouldChageCharacter delegate method set text to data array with respective indexpath and create the object of uitableviewcell and set text to the respective label. for get data you can get data from array. In above answer may be if u scroll table then TableCell will mix with eachother.
Related
here is a table View with two Different Section Inside VC2 to Modify or add Contact into phone . the problem it is text fields become blank in Sections when table view scrolled . i found a way to fix this Problem in section 1 but i cant Handle section 2 .
Model :
class ContactModel : NSObject {
var identifier : String!
var thumbnailImageData : UIImage?
var givenName : String!
var familyName : String!
var phoneNumbers : [String]!
var emailAddresses : [String]!
override init() {
self.phoneNumbers = []
self.emailAddresses = []
super.init()
}
VC2 :
var contactModel = ContactModel()
#IBOutlet weak var tvInsert: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell0 = tableView.dequeueReusableCell(withIdentifier: "InsertTableCell0") as! InsertTableCell0
cell0.txtFirstName.text = self.contactModel.givenName
cell0.txtLastName.text = self.contactModel.familyName
return cell0
}else if indexPath.section == 1 {
let cell1 = tableView.dequeueReusableCell(withIdentifier: "InsertTableCell1") as! InsertTableCell1
cell1.btnDelete.addTarget(self, action: #selector(deleteRowDate(_:)), for: .touchUpInside)
cell1.txtPhoneNumber.placeholder = "Phone Number"
cell1.txtPhoneNumber.text = contactModel.phoneNumbers[indexPath.row]
cell1.txtPhoneNumber.delegate = self
cell1.txtPhoneNumber.tag = indexPath.row
return cell1
}else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "InsertTableCell2") as! InsertTableCell2
cell2.btnEmail.addTarget(self, action: #selector(deleteRowDate(_:)), for: .touchUpInside)
cell2.txtEmail.placeholder = "Email"
cell2.txtEmail.text = contactModel.emailAddresses[indexPath.row]
cell2.txtEmail.delegate = self
cell2.txtEmail.tag = indexPath.row
return cell2
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let aText = textField.text {
self.contactModel.phoneNumbers[textField.tag] = aText
}
}
Seems like all the fields delegates are self and textFieldDidEndEditing is setting data for all the fields instead of phone number fields.
you have to check if the textField is phone number or not and also you should update the data in model when textField text is changed instead of end editing.
Easier solution will be :- Remove this line "cell1.txtPhoneNumber.delegate = self"
Replace var phoneNumbers: [String]! in model with following
var phoneNumbers = ["","",""] //for 3 phone numbers
Put this code in cellForRow of the particular cell i.e (indexPath.section == 1) for the above question
//Saved Phone number in model
cell.txtPhoneNumber.text = contactModel.phoneNumbers[indexPath.row]
//Get and save Phone number when changed
cell.txtPhoneNumber.tag = indexPath.row
cell.txtPhoneNumber.addTarget(self, action: #selector(self.phoneNumberChanged(_:)), for: .editingChanged)
In the ViewController
#objc func phoneNumberChanged(_ sender: UITextField){
//Phone number changed
contactModel.phoneNumbers[sender.tag] = sender.text ?? ""
}
In case you want to save the phone number on end Editing replace .editingChanged with .editingDidEnd
I have a Form (tableview with questions and textfields).
It is not static as data may change depending upon the form user select.
So I store these questions and validation constraints in a dictionary.
How can I validate these fields before user press submit ?
Code is given below :
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let Cell:FormTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: "cellformid") as! FormTableViewCell
let dict = questions?[indexPath.row]
Cell.form_question_label.text = dict?.question_name
Cell.form_answer_textfield.text = dict?.answer ?? ""
return Cell
}
//Submit Button Action
#IBAction func saveFormButtonPressed(_ sender: Any) {
//Here I do operations to save a Array which contain all attended answers.
}
NOTE : When I press submit button data entered by user is being saved to database, but Validation cannot be done,because if some question is unattended that is not stored in database and also is not attached to the Array. So I cannot check whether all Mandatory fields are completed.
Please suggest a method..
You can add to your UITextField:
textField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
and then validate answers:
#objc func textFieldDidChange(_ textField: UITextField) {
//Validation
}
Try this.
for view in self.view.subviews {
if (view is UITextField) {
var textfield = view as! UITextField
if (textfield.text == "") {
// execute code
}
}
}
I have created a custom tableViewCell class for a prototype cells which is holding a text field. Inside ThirteenthViewController, I would like to reference the tableViewCell class so that I can access its doorTextField property in order to assign to it data retrieved from UserDefaults. How can I do this?
class ThirteenthViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate {
var options = [
Item(name:"Doorman",selected: false),
Item(name:"Lockbox",selected: false),
Item(name:"Hidden-Key",selected: false),
Item(name:"Other",selected: false)
]
let noteCell:NotesFieldUITableViewCell! = nil
#IBAction func nextButton(_ sender: Any) {
//save the value of textfield when button is touched
UserDefaults.standard.set(noteCell.doorTextField.text, forKey: textKey)
//if doorTextField is not empty assign value to FullData
guard let text = noteCell.doorTextField.text, text.isEmpty else {
FullData.finalEntryInstructions = noteCell.doorTextField.text!
return
}
FullData.finalEntryInstructions = "No"
}
override func viewDidLoad() {
let index:IndexPath = IndexPath(row:4,section:0)
let cell = tableView.cellForRow(at: index) as! NotesFieldUITableViewCell
self.tableView.delegate = self
self.tableView.dataSource = self
cell.doorTextField.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return options.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// configure the cell
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
if indexPath.row < 3 {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
cell.textLabel?.text = options[indexPath.row].name
return cell
} else {
let othercell = tableView.dequeueReusableCell(withIdentifier: "textField") as! NotesFieldUITableViewCell
othercell.doorTextField.placeholder = "some text"
return othercell
}
}
}//end of class
class NotesFieldUITableViewCell: UITableViewCell {
#IBOutlet weak var doorTextField: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
In order to access the UITextField in your cell, you need to know which row of the UITableView contains your cell. In your case, I believe the row is always the 4th one. So, you can create an IndexPath for the row and then, you can simply do something like this:
let ndx = IndexPath(row:3, section: 0)
let cell = table.cellForRow(at:ndx) as! NotesFieldUITableViewCell
let txt = cell.doorTextField.text
The above might not be totally syntactically correct since I didn't check for syntax, but I'm sure you can take it from there, right?
However, do note that in order for the above to work, the last row (row 4) has to be always visible. If you try to fetch rows which are not visible, you do run into issues with accessing them since UITableView reuses cells and instantiates cells for the visible rows of data.
Also, if you simply want to get the text that the user types and text input ends when they tap "Enter", you can always simply bypass accessing the table row at all and add a UITextFieldDelegate to your custom cell to send a notification out with the entered text so that you can listen for the notification and take some action.
But, as I mentioned above, this all depends on how you have things set up and what you are trying to achieve :)
Update:
Based on further information, it appears as if you want to do something with the text value when the nextButton method is called. If so, the following should (theoretically) do what you want:
#IBAction func nextButton(_ sender: Any) {
// Get the cell
let ndx = IndexPath(row:4, section: 0)
let cell = table.cellForRow(at:ndx) as! NotesFieldUITableViewCell
//save the value of textfield when button is touched
UserDefaults.standard.set(cell.doorTextField.text, forKey: textKey)
//if doorTextField is not empty assign value to FullData
guard let text = cell.doorTextField.text, text.isEmpty else {
FullData.finalEntryInstructions = cell.doorTextField.text!
return
}
FullData.finalEntryInstructions = "No"
}
You can create a tag for the doorTextField (for instance 111)
Now you can retrieve the value.
#IBAction func nextButton(_ sender: Any) {
//save the value of textfield when button is touched
guard let textField = self.tableViewview.viewWithTag(111) as! UITextField? else { return }
prit(textField.text)
.....
}
I am working on this app, and I am new to Swift. Only two weeks of knowledge. I am supposed to create a table view with 12 cells - 3 of which are supposed to be text fields and user can type what they want. I have made two prototype cells with two different identifiers. I am using an array called "items" which has strings to be represented in the cells. If the string is blank, that cell is supposed to be a text field, which I have done. Problem occurs after that when I try to type in those fields and scroll the cells.
Please help me in understanding and solving the following issues:
How can I delegate the text field which I added as a subview to my tableview cell?
How can I make sure that whatever I type in the textfield remains there, even after I scroll the cells up and down as I wish?
How can I make sure user can edit whatever they type in the text field?
Here is my code:
var items = ["Apple", "Fish", "Dates", "Cereal", "Ice cream", "Lamb", "Potatoes", "Chicken", "Bread", " ", " "," "]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var iD = "normal"
if (items[indexPath.row] == " ") {
iD = "textField"
let cell = tableView.dequeueReusableCellWithIdentifier(iD, forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
let textField = UITextField(frame: CGRect(x: 20, y: 10, width: 150, height: 30))
textField.backgroundColor = UIColor.brownColor()
textField.placeholder = ""
cell.contentView.addSubview(textField)
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier(iD, forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
////
func tableView(tableView: UITableView, moveRowAtIndexPath sourceIndexPath: NSIndexPath, toIndexPath destinationIndexPath: NSIndexPath) {
if (sourceIndexPath.row != destinationIndexPath.row){
let temp = items[sourceIndexPath.row]
items.removeAtIndex(sourceIndexPath.row)
items.insert(temp, atIndex: destinationIndexPath.row)
}
tableView.reloadData()
}
I would rather create a subclass of UITableviewCell and add a textfield there as a subview. Then you set the delegate of the textfield to the cell itself.
Here is some sample code. I did not try to run it, but i think it should work:
class InputCell: UITableViewCell, UITextFieldDelegate {
private let textField = UITextField()
private var resultBlock: ((changedText: String) -> ())? = nil
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.textField.delegate = self
self.contentView.addSubview(self.textField)
// additional setup for the textfield like setting the textstyle and so on
}
func setup(initalText: String, resultBlock: (changedText: String) -> ()) {
self.textField.text = initalText
self.resultBlock = resultBlock
}
func textFieldDidEndEditing(textField: UITextField) {
if let block = self.resultBlock, let text = textField.text {
block(changedText: text)
}
}
}
In your view controller i would change the items to be a dictionary, so you can directly link them to the indexpaths of the tableview. And you need to let the tableview register your custom cell class.
var items = [0: "Apple", 1: "Fish", 2: "Dates", 3: "Cereal", 4: "Ice cream", 5: "Lamb", 6: "Potatoes", 7: "Chicken", 8: "Bread", 9: " ", 10: " ", 11: " "]
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
// the position of the cell with a textfield is fixed right? Just put the row number here then
if indexPath.row > 8 {
let cell = tableView.dequeueReusableCellWithIdentifier("CellWithTextField", forIndexPath: indexPath) as! InputCell
if let initialText = items[indexPath.row] {
cell.setup(initialText) { [weak self] text in
self?.items[indexPath.row] = text
}
}
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier("NormalCell", forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
first, add the textfield protocol UITextFielDelegate, and in cell for row atindex write textfield.delegate = self.
And add delegate method to manage the text.
Hope this help you.
You have to save the textfield data in some object. As UITableView reuses same cells your textfield text will be lost on scroll as the cell will be reused. I would suggest creating some array and storing the data in that for each indexPath.
You should implement the UITextField delegate methods inside cell class and whenever some text changes, you should call another delegate method of yours (CellDelegate) which should be implemented in ViewController to update data.
This delegate method will pass text and indexPath of cell.
In the UITableViewCell add the UItextfield delegate for textChange.
You can alter your items array to contain model objects having the real value and the editing value.
let apple = Item()
apple.realValue = "Apple"
apple.editedValue = ""
var item = [0: apple, .............
Pass the model object in the tableview cell and update the edited value on text change delegate call. In cellForRow add the editedValue in the textfield text property
let item = items[indexPath.row]
cell.textLabel?.text = item.realValue
if item.editedValue{
// add the textFieldText
}
Inherit UITextFieldDelegate to your controller
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var iD = "normal"
if (items[indexPath.row] == " ") {
iD = "textField"
let cell = tableView.dequeueReusableCellWithIdentifier(iD, forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
let textField = UITextField(frame: CGRect(x: 20, y: 10, width: 150, height: 30))
textField.backgroundColor = UIColor.brownColor()
textField.placeholder = ""
cell.contentView.addSubview(textField)
textField.delegate = self // set delegate
textField.tag= indexPath.row
return cell
}
else {
let cell = tableView.dequeueReusableCellWithIdentifier(iD, forIndexPath: indexPath)
cell.textLabel?.text = items[indexPath.row]
return cell
}
}
//delegate methods
func textFieldDidBeginEditing(textField: UITextField!) { //delegate method
print(textField.tag)//you will get the row. items[textField.tag] will get the object
}
func textFieldShouldEndEditing(textField: UITextField!) -> Bool { //delegate method
print(textField.tag)//you will get the row. items[textField.tag] will get the object
items[textField.tag]= textField.text
return false
}
func textFieldShouldReturn(textField: UITextField!) -> Bool { //delegate method
textField.resignFirstResponder()
return true
}
I'm making a very simple app where the user enters the number of people in the first Screen.
In the second screen it generates a number of UITableViewCell based on the number the user entered in the first screen. The UITableViewCell have a UITextField in them and I'm trying to store the data entered in those fields in an array once the user clicks to go to the third screen.
How can I do that? Thanks in advance!
Edit: I'm using the storyboard.
Here is what the code that calls for the custom UITableViewCell looks like for my UIViewController:
func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{
var cell: EditingCell = tableView.dequeueReusableCellWithIdentifier("Cell") as EditingCell
if indexPath.row % 2 == 0{
cell.backgroundColor = UIColor.purpleColor()
} else {
cell.backgroundColor = UIColor.orangeColor()
}
let person = arrayOfPeople[indexPath.row]
cell.setCell(person.name)
return cell
}
Here is what the code for the UITableViewCell looks like:
class EditingCell: UITableViewCell{
#IBOutlet weak var nameInput: UITextField!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func setCell(name:String){
self.nameInput.placeholder = name
}
}
There is a problem with your approach if the number of rows in your table exceeds the number that can fit on screen. In that case, the cells that scroll off-screen will be re-used, and the contents of the nameInput textField will be lost. If you can be sure that this will never happen, use the following code (in the method that handles button taps) to compose your array:
var arrayOfNames : [String] = [String]()
for var i = 0; i<self.arrayOfPeople.count; i++ {
let indexPath = NSIndexPath(forRow:i, inSection:0)
let cell : EditingCell? = self.tableView.cellForRowAtIndexPath(indexPath) as EditingCell?
if let item = cell?.nameInput.text {
arrayOfNames.append(item)
}
}
println("\(arrayOfNames)")
Alternatively....
However, if it is possible that cells will scroll off-screen, I suggest a different solution. Set the delegate for the nameInput text fields, and then use the delegate methods to grab the names as they are entered.
First, add variables to your view controller, to hold the array and the row number of the text field currently being edited.
var arrayOfNames : [String] = [String]()
var rowBeingEdited : Int? = nil
Then, in your cellForRowAtIndexPath method, add:
cell.nameInput.text = "" // just in case cells are re-used, this clears the old value
cell.nameInput.tag = indexPath.row
cell.nameInput.delegate = self
Then add two new functions, to catch when the text fields begin/end editing:
func textFieldDidEndEditing(textField: UITextField) {
let row = textField.tag
if row >= arrayOfNames.count {
for var addRow = arrayOfNames.count; addRow <= row; addRow++ {
arrayOfNames.append("") // this adds blank rows in case the user skips rows
}
}
arrayOfNames[row] = textField.text
rowBeingEdited = nil
}
func textFieldDidBeginEditing(textField: UITextField) {
rowBeingEdited = textField.tag
}
When the user taps the button, they might still be editing one of the names. To cater for this, add the following to the method that handles the button taps:
if let row = rowBeingEdited {
let indexPath = NSIndexPath(forRow:row, inSection:0)
let cell : EditingTableViewCell? = self.tableView.cellForRowAtIndexPath(indexPath) as EditingTableViewCell?
cell?.nameTextField.resignFirstResponder()
}
This forces the textField to complete editing, and hence trigger the didEndEditing method, thereby saving the text to the array.
Here for new swift versions of answer
var arrayOfNames : [String] = [String]()
var i = 0
while i < taskArrForRead.count {
let indexPath = IndexPath(row: i, section: 0)
let cell : taslakDuzenlemeCell? = self.tableView.cellForRow(at: indexPath) as! taslakDuzenlemeCell?
if let item = cell?.taslakTextField.text {
arrayOfNames.append(item)
}
i = i + 1
}
print("\(arrayOfNames)")