How to get textFields from static cells in UITableViewController? Swift
Well first I can give you an answer that might satisfy you and fix your loop but I would recommend not doing it that way to alter your textfields. I would recommend doing it in cellForRow even though they may be static cells. Depending on your view setup in the cells it would look like this if the textfield is added directly to the cells and not to another view.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Testing")
for cell in tableView.visibleCells{
for sub in cell.contentView.subviews{
if sub is UITextField{
print("Textfield")
}
}
}
}
Read Data From Text Field in TableView Dynamic Cell in Swift
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)
}
How to get the textfield value of each dynamically created cell in UITableView
There seems to be a problem with your code:
[cell addSubview:scheduelText];
[cell addSubview:dateText];
[cell addSubview:accountText];
Everytime you dequeue the cell will add a new subview to the cell,but never removed.
In my suggestion,you'd better identifier 4 kind of cells with different identifiers,or removesubviews whenever dequeue the cell.
Add cell to UITableView if textfield of previous cell is filled in
You can check, whether textfield is empty or not in viewController itself. You just confirm the delegate for textfield from storyboard and in cellForRowAt and give the tag to that text field
e.g,
cell.yourTextField.delegate = self
cell.yourTextField.tag = indexPath.row
and check the textfield is empty or not in textField delegate method. Create object of cell in method like
func textFieldDidEndEditing(_ textField: UITextField) {
rowBeingEditedInt = textField.tag
let indexPath = IndexPath(row:rowBeingEdited! , section:0 )
let cell = yourTableView.cellForRow(at: indexPath) as! yourTableViewCell
// check cell.yourtextField.text isempty and do your condition
}
Get all values from tableViewCell from TextFields
TextFields in TableView cells can be tricky. First, use the data model and reloadData() instead of insertRows(at:) like this:
@objc func addHourlyRate(sender: UIButton) {
let newRow: String = ""
secondRowText.append(newRow)
self.tableView.reloadData()
}
Now, set the text in the cells, and tag the UITextField with the row number in cellForRowAt() like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let hourlyRateCell = tableView.dequeueReusableCell(withIdentifier: "hourlyRateCell", for: indexPath) as! SettingsHourlyRateCell
hourlyRateCell.rubHourTF.text = secondRowText[indexPath.row]
hourlyRateCell.rubHourTF.tag = indexPath.row
return hourlyRateCell
}
Next, use UITextFieldDelegate to keep track of any changes in the text fields, something like:
func textFieldDidEndEditing(_ textField: UITextField) {
let tableRow = textField.tag
secondRowText[tableRow] = textField.text
}
Notice how the textField tag that was set in cellForRow() is now used to know which row the textField is at in the table.
How to simultaneously change textField.text in a prototype cell?
At a high level:
- You need to have a model to store your data being updated
- You need to observe the text being entered using
UITextFieldDelegate
- As your text is being entered, update all rows except active cell with new calculations
The most interesting work is in textFieldDidChangeSelection
and cellForRowAt indexPath
Here is an example of how I would do something like this:
1. Set up some custom types
// Your model to store data in text fields
// Maybe for you, you will store currencies
// and base currency to multiply against
// I am just storing a random number for example
struct FinanceModel
{
var number: Int?
}
// Custom UITextField which will store info
// about cell index path as we need to identify
// it when editing
class MappedTextField: UITextField
{
var indexPath: IndexPath!
}
2. Example of UITableView Cell, you can ignore if you have your own
fileprivate class CustomCell: UITableViewCell
{
// Use custom text field
var textField: MappedTextField!
static let tableViewCellIdentifier = "cell"
override init(style: UITableViewCell.CellStyle,
reuseIdentifier: String?)
{
super.init(style: style,
reuseIdentifier: reuseIdentifier)
configureTextField()
}
required init?(coder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
// Configure text field and auto layout
private func configureTextField()
{
textField = MappedTextField()
textField.keyboardType = .numberPad
textField.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(textField)
textField.layer.borderWidth = 2.0
textField.layer.borderColor = UIColor.blue.cgColor
textField.layer.cornerRadius = 5.0
// auto-layout
textField.leadingAnchor.constraint(equalTo: contentView.leadingAnchor,
constant: 20).isActive = true
textField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor,
constant: -20).isActive = true
textField.topAnchor.constraint(equalTo: contentView.topAnchor,
constant: 20).isActive = true
textField.bottomAnchor.constraint(equalTo: contentView.bottomAnchor,
constant: -20).isActive = true
}
}
3. Set up in view controller - you can skip if already set up, just here for completeness
class InputViewController: UIViewController
{
let tableView = UITableView()
// Initialize your mode
var financeModel = FinanceModel()
// This will store current editing cell which is active
var activeTextFieldIndexPath: IndexPath?
override func viewDidLoad()
{
super.viewDidLoad()
// This is just view set up for me,
// You can ignore this
title = "TableView Input"
navigationController?.navigationBar.tintColor = .white
view.backgroundColor = .white
configureTableView()
}
// Configure TableView and layout
private func configureTableView()
{
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(CustomCell.self,
forCellReuseIdentifier: CustomCell.tableViewCellIdentifier)
tableView.dataSource = self
tableView.delegate = self
// remove additional rows
tableView.tableFooterView = UIView()
view.addSubview(tableView)
// Auto layout
tableView.leadingAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
tableView.topAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.trailingAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.bottomAnchor
.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
}
}
4. TableView DataSource
extension InputViewController: UITableViewDataSource
{
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
// Random number for me
return 10
}
func tableView(_ tableView: UITableView,
cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell =
tableView.dequeueReusableCell(withIdentifier:
CustomCell.tableViewCellIdentifier)
as! CustomCell
// Set index path of custom text field
cell.textField.indexPath = indexPath
// Set view controller to respond to text field events
cell.textField.delegate = self
// Check if any cell is currently active
// && if we are current cell is NOT the active one
// We want to leave active cell untouched
if let activeTextFieldIndexPath = activeTextFieldIndexPath,
activeTextFieldIndexPath.row != indexPath.row
{
updateCell(cell, atIndexPath: indexPath)
}
else
{
// no active cell, just set the text
updateCell(cell, atIndexPath: indexPath)
}
return cell
}
private func updateCell(_ cell: CustomCell,
atIndexPath indexPath: IndexPath)
{
// Retrieve number currently stored in model
if let number = financeModel.number
{
// Set text of number in model * row number
// Do any calculation you like, this is just an
// example
cell.textField.text =
"\(number) x \(indexPath.row) = \(number * indexPath.row)"
}
else
{
// If no valid number, set blank
cell.textField.text = ""
}
}
}
5. TextField Delegate
extension InputViewController: UITextFieldDelegate
{
// Respond to new text in the text field
func textFieldDidChangeSelection(_ textField: UITextField)
{
// 1. Convert generic UITextField to MappedTextField
// 2. && Retrieve index path from custom MappedTextField
// 3. && Retrieve text from the text field
// 4. && Check if text is valid number
if let textField = textField as? MappedTextField,
let indexPath = textField.indexPath,
let text = textField.text,
let number = Int(text)
{
// Assign local variable with index path we are editing
activeTextFieldIndexPath = indexPath
// Update number in the financial model
financeModel.number = number
// We only want to update data in visible rows so
// get all the index paths of visible rows
let visibleRows = self.tableView.indexPathsForVisibleRows ?? []
// We want to update all rows EXCEPT active row
// so do a filter for this
let allRowsWithoutActive = (visibleRows).filter
{
// Remove the active index path from the
// visible index paths
$0.section != indexPath.section ||
$0.row != indexPath.row
}
// Reload the visible rows EXCEPT active
self.tableView.reloadRows(at: allRowsWithoutActive,
with: .automatic)
}
}
// This is just to make current text field
// empty when editing, you can ignore
func textFieldDidBeginEditing(_ textField: UITextField)
{
textField.text = ""
}
// Reset when text field is no longer being edited
func textFieldDidEndEditing(_ textField: UITextField)
{
activeTextFieldIndexPath = nil
tableView.reloadData()
}
}
6. TableView Delegate
extension InputViewController: UITableViewDelegate
{
func scrollViewDidScroll(_ scrollView: UIScrollView)
{
// End editing when scrolling table view
// This is for me, you can have another way
view.endEditing(true)
}
func tableView(_ tableView: UITableView,
heightForRowAt indexPath: IndexPath) -> CGFloat
{
// Return a random height
return 80
}
}
End result is something like this:
Hope this gives you enough to set you on your way.
I could not get text field value in dynamic table view swift
Cells are reused, when you scroll and if visibility of cell goes out of screen area, then they are reused.
You must have to take temporary array to save values and make them in sync with cell indexes. Set those values in cellForRowAt Index path method from array you created.
Related Topics
Combine Sink: Ignore Receivevalue, Only Completion Is Needed
How to Set First Responder for Nstextview in Swift
Localizing The Reading of Emoji on iOS 10.0 or Higher
How to Hide The Status Bar Programmatically in iOS 8
Navigation Link in Bar Items Goes Back to Top of Navigationview
Why Is The Leading Swipe Action Also Duplicated as a Trailing Action
Iterating Through an Array of Strings, Fetched from Mongodb
Inner Didset Protection Bizarrely Extends to The Whole Class
Nsmanagedobjectcontext's Propagatesdeletesatendofevent Set to False Causes Error on Save
Carthage Update Error: "Github API Request Failed: Bad Credentials"
Ambiguous Use of 'Filter' When Converting Project to Swift 4
Missing Required Module Firebase - Jenkins Build Error
iOS 16 Swiftui List Background
Errors Encountered While Discovering Extensions: Error Domain=Pluginkit Code=13 "Query Cancelled"
Location Access Request in iOS 11
How to Handle Error with Realm During Writing
How to Get The Coordinates of The Point on a Line That Has The Smallest Distance from Another Point