'Read Data From Text Field in TableView Dynamic Cell in Swift
I want to read the text in the text fields in my dynamic cells in the table view.
Each cell has two text fields and the user can add more cells.
My question : How can I read the values from text fields (textField1 & textField2) and add them to my cards struct?
In the Cards struct there are two variables. frontLabel and backLabel as string
My table View:
class AddFCViewController: UIViewController {
var numberOfTextFields = 1
var cards = [Cards]()
// Table View
let tableView : UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.backgroundColor = .purple
return tableView
}()
// Add Button
let addButtton : UIButton = {
let btn = UIButton()
btn.translatesAutoresizingMaskIntoConstraints = false
btn.backgroundColor = .link
btn.setTitle("Ekle", for: .normal)
return btn
}()
override func viewDidLoad() {
super.viewDidLoad()
configureTableView()
configureAddButton()
}
// MARK: - Configure Add Button
func configureAddButton() {
view.addSubview(addButtton)
addButtton.addTarget(self, action: #selector(addButtonTapped(sender:)), for: .touchUpInside)
}
// MARK: - Add Button Tapped MEthod
@objc func addButtonTapped(sender: UIButton){
numberOfTextFields += 1
tableView.reloadData()
}
// MARK: - Configure Table View
func configureTableView() {
view.addSubview(tableView)
setTableViewDelegates()
tableView.rowHeight = 100
tableView.register(AddFCCell.self, forCellReuseIdentifier: "AddFCCell")
}
// MARK: - Table View Delegates
func setTableViewDelegates() {
tableView.delegate = self
tableView.dataSource = self
}
}
// MARK: - Table View Extension & Delegate Methods
extension AddFCViewController: UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfTextFields
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AddFCCell") as! AddFCCell
cell.selectionStyle = .none
cell.textField1.tag = indexPath.row
cell.textField2.tag = indexPath.row
print(cell.textField1.tag)
print(cell.textField2.tag)
// cell.textField1.text = cards[indexPath.row].frontLabel
// cell.textField2.text = cards[indexPath.row].backLabel
cell.textField1.delegate = self
cell.backgroundColor = .purple
return cell
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return 10
}
func textFieldDidBeginEditing(_ textField: UITextField) {
// textField.addTarget(self, action: #selector(valueChanged), for: .editingChanged)
}
}
Here is my custom Cell Class.
class AddFCCell: UITableViewCell {
// MARK: - Text Field 1
let textField1 : UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.backgroundColor = .white
textField.isUserInteractionEnabled = true
textField.placeholder = "TYPE HERE SOMETHING"
textField.font = UIFont.systemFont(ofSize: 20)
return textField
}()
// MARK: - Text Field 2
let textField2 : UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.backgroundColor = .white
textField.isUserInteractionEnabled = true
textField.placeholder = "TYPE HERE SOMETHING"
textField.font = UIFont.systemFont(ofSize: 20)
return textField
}()
// MARK: - view Did Load
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(textField1)
contentView.addSubview(textField2)
configureTextBox1(textField: textField1)
configureTextBox2(textField: textField2)
}
// MARK: - Configure Text Box 1 Function
func configureTextBox1(textField : UITextField) {
textField.layer.cornerRadius = 20
}
// MARK: - Configure Text Box 2 Function
func configureTextBox2(textField : UITextField) {
textField.layer.cornerRadius = 20
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
PS - I deleted some of my constraints code as it was just too much code there.
Solution 1:[1]
I would change couple of things,
1. Get rid of numberOfTextFields
2. Use var cards = [Cards]()
to return the number of cells in tableView
3. Initialize cards
with a single Cards
instance and set its property to nil. So I would have Cards implementation as
struct Cards {
var frontLabel: String?
var backLabel: String?
}
4. Initialize cards
array as
var cards = [Cards()]
5. Return number of rows from arrays count
func numberOfSections(in tableView: UITableView) -> Int {
return cards.count
}
6. I would migrate text field delegate responsibilities to Cell instead of moving it to ViewController and I would have a delegate in AddFCCell
to update the ViewController about text changes
@objc protocol AddFCCellDelegate {
func updateCard(with frontText: String?, backText: String?, for cell: AddFCCell)
}
class AddFCCell: UITableViewCell {
weak var delegate:AddFCCellDelegate?
//.... other codes of yours
}
extension AddFCCell: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
self.delegate?.updateCard(with: self.textField1.text, backText: self.textField2.text, for: self)
}
}
7. Finally, implement/confirm to AddFCCellDelegate
in viewController and reload TableView bu updating text
extension ViewController: AddFCCellDelegate {
func updateCard(with frontText: String?, backText: String?, for cell: AddFCCell) {
guard let indexPath = self.tableView.indexPath(for: cell) else {
return
}
self.cards[indexPath.row].frontLabel = frontText
self.cards[indexPath.row].backLabel = backText
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
8. Update CellForRowAtIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "AddFCCell") as! AddFCCell
cell.selectionStyle = .none
cell.delegate = self
cell.textField1.text = cards[indexPath.row].frontLabel
cell.textField2.text = cards[indexPath.row].backLabel
cell.backgroundColor = .purple
return cell
}
You can obviously implement a efficient protocol and return/update only specific variable in Cards struct based on which textField was modified n all, but the code posted here is only to give you an idea and to get started with
EDIT 1:
Make sure you have set your TextFields delegate right
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(textField1)
textField1.delegate = self
contentView.addSubview(textField2)
textField2.delegate = self
configureTextBox1(textField: textField1)
configureTextBox2(textField: textField2)
}
Solution 2:[2]
You can add placeholder text to both text filed, make sure both place holder text are different, here I am trying to assign uniq identifier to textfield with tag.
&
retrieve text field value by comparing place holder text in textFieldDidBeginEditing.
cell.textField1.tag = indexPath.row
cell.textField2.tag = indexPath.row
cell.textField1.placeholder = "1"
cell.textField2.placeholder = "2"
func textFieldDidBeginEditing(_ textField: UITextField) {
if(textField.tag && placeholder){
}
}
It is not a proper solution but by this way you can differentiate both text filed with same tag.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Arasuvel |