I am trying to read the input from 2 textfield from multiple TableViewCell, Looks like it only access input2 and append input2 twice to the inputRead array. How could I fix this? Many thanks
P/S: This is how I layout 2 TextFields in a cell: [input1] [input2]
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
customCell.input1.tag = indexPath.row
customCell.input2.tag = indexPath.row
customCell.input1.delegate = self
customCell.input2.delegate = self
return customCell
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
return textField.endEditing(true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
let indexPath = IndexPath(row: textField.tag, section: 0)
if let customCell = self.table.cellForRow(at: indexPath) as?
CustomCell {
let string1 = String(customCell.input1.text!)
let string2 = String(customCell.input2.text!)
inputRead.append(input(string1, string2))
}
}//end class
class CustomCell : UITableViewCell{
#IBOutlet weak var input1: UITextField!
#IBOutlet weak var input2: UITextField!
}
Related
What is the best practice to add dynamic list of items in UITableViewCell with showMore/showLess?
UITableView inside UITableViewCell
I used table view inside my UITableViewCell , but I have issue with reload row at indexPath causes jumpy scrolling.
TableViewController Code :
class ViewController: UIViewController , UITableViewDelegate , UITableViewDataSource , delegatCell {
#IBOutlet weak var tableView: UITableView!
var elementsArray: [data] = []
override func viewDidLoad() {
super.viewDidLoad()
for i in 1...20 {
elementsArray.append(data.init(title: "title\(i)", date: "8:00 PM", isShowMoreClicked: false, elements: ["ToDo\(i)","Aqraa\(i)","ToDo\(i)","ToDo\(i)" , "Mobile Team\(i)" , "Testing Data\(i)"]))
// Do any additional setup after loading the view, typically from a nib.
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return elementsArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let obj = elementsArray[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! expandedTableViewCell
cell.initializeCell()
print(obj.elements)
cell.data = obj.elements
cell.delegate = self
cell.dateLabel.text = obj.date
cell.titleLabel.text = obj.title
cell.showMore.titleLabel?.font = reqularDynamicFont()
cell.showMore.titleLabel?.adjustsFontSizeToFitWidth = true
cell.showMore.tag = indexPath.row
cell.isShowMoreClicked = obj.isShowMoreClicked
cell.tableView.reloadData()
self.view.layoutIfNeeded()
return cell
}
func reqularDynamicFont()-> UIFont{
let font = UIFont.systemFont(ofSize: 15, weight: .regular)
let fontMetrics = UIFontMetrics(forTextStyle: .caption1)
return fontMetrics.scaledFont(for: font)
}
func reloadTableView(indexpath: IndexPath , isShowMoreClicked: Bool) {
elementsArray[indexpath.row].isShowMoreClicked = isShowMoreClicked
tableView.reloadRows(at: [indexpath], with: .automatic)
}
}
ExpandedTableViewCell with inner table view Code :
import UIKit
protocol delegatCell {
func reloadTableView(indexpath: IndexPath , isShowMoreClicked: Bool)
}
class expandedTableViewCell: UITableViewCell , UITableViewDelegate , UITableViewDataSource{
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var showMore: UIButton!
var data : [String] = []
var initalNum = 2
var isShowMoreClicked: Bool = false
var delegate:delegatCell?
func initializeCell(){
data.removeAll()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if isShowMoreClicked{
return data.count
}else{
return initalNum
}
}
#IBAction func reloadData(_ sender: UIButton) {
delegate?.reloadTableView(indexpath: IndexPath(row: sender.tag, section: 0), isShowMoreClicked: !isShowMoreClicked)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print(data)
let cell = tableView.dequeueReusableCell(withIdentifier: "simpleTableViewCell", for: indexPath) as! simpleTableViewCell
cell.descLabel.text = data[indexPath.row]
return cell
}
}
I used auto layout to support dynamic height.
As the title said; I want to read the data from multiple UITextFields for each cell and store them in an array. How would I do so?
I have created a subclass CustomCell that has 2 UITextFields in it. P/S: the identifier for the cell is also CustomCell
Many thanks
class TableViewController : UITableViewController, UITextFieldDelegate {
var data = [input]()
#IBOutlet var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell") as! CustomCell
customCell.input1.tag = indexPath.row
customCell.input2.tag = indexPath.row
customCell.input1.delegate = self
customCell.input2.delegate = self
return customCell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
#IBAction func addButton(_ sender: Any) {
table.beginUpdates()
table.insertRows(at: [IndexPath(row: data.count-1, section: 0)], with: .automatic)
table.endUpdates()
}
func textFieldDidEndEditing(_ textField: UITextField) {
let indexPath = IndexPath(row: textField.tag, section: 0)
if let customCell = self.table.cellForRow(at: indexPath) as? CustomCell{
let a = customCell.Credit.text!.isEmpty ? no:String(customCell.input1.text!)
let b = customCell.letterGrade.text!.isEmpty ? no:String(customCell.input2.text!))
inputRead.append(input(string1: a, string2: b))
}
#IBAction func foo(_ sender: Any) {
if inputRead.count == 0{
return
}
//the rest of implementation
}
CustomCell class:
Import UIKit
public class CustomCell: UITableViewCell {
#IBOutlet weak var input1: UITextField!
#IBOutlet weak var input2: UITextField!
}
Update your code as follow in your view controller.
Assign textfield's delegate self to ViewController in cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
customCell.input1.delegate = self
customCell.input2.delegate = self
return customCell
}
Now implemented text field delegate in your view controller.
func textFieldDidEndEditing(_ textField: UITextField) {
guard let jobTaskCell = textField.superview?.superview as? CustomCell else {
return
}
if textField == jobTaskCell.input1 {
// Get text from textfield and store in array
} else if textField == jobTaskCell.input2 {
// Get text from textfield and store in array
}
}
Note: Following code depends on how to place textfield in your cell. So make sure you need to check this recursively by adding and removing superView
guard let jobTaskCell = textField.superview?.superview as? CustomCell else {
return
}
This simply mean that, just for when textfield inside tableview cell without any extra view:
textField.superview = contentView of TableViewCell
textField.superview?.superview = TableViewCell
I hope this will fix your issue.
This can help you to store text in an array:-
1: Add indexPath.row as a tag to your textField in cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let customCell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
customCell.input1.tag = indexPath.row
customCell.input2.tag = indexPath.row
customCell.input1.delegate = self
customCell.input2.delegate = self
return customCell
}
2: In textField delegate method you can get your textField text
func textFieldDidEndEditing(_ textField: UITextField) {
let indexPath = IndexPath(row: textField.tag, section: 0)
if let customCell = self.table.cellForRow(at: indexPath) as? CustomCell {
//From here you can get your particular input1 or input2 textfield text
print(customCell.input1.text)
print(customCell.input2.text)
}
}
I have created a tableView with two different labels and one textfield. Depending on the indexPath in which is selected the labels will display different text according to the array. I have created a CocoTouch Class file and made it type TableViewCell.
TableViewCell.swift
import UIKit
class Driver_Navigation_TableViewCell: UITableViewCell {
#IBOutlet weak var orderTextField: UITextField!
#IBOutlet weak var adressLabel: UILabel!
#IBOutlet weak var nameLabel: UILabel!
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
}
}
ViewController.swift
class ViewController: UIViewController, MGLMapViewDelegate, UITableViewDataSource, UITableViewDelegate, UITextFieldDelegate {
var allCellsText = [String]()
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Driver_Navigation_TableViewCell
cell.adressLabel.text = passengersAdress[indexPath.row]
cell.nameLabel.text = passengersName[indexPath.row]
cell.orderTextField.placeholder = "\(indexPath.row + 1)."
return(cell)
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath) as! Driver_Navigation_TableViewCell
cell.orderTextField.tag = indexPath.row
cell.orderTextField.delegate = self // theField is your IBOutlet UITextfield in your custom cell
return cell
}
func textFieldDidEndEditing(textField: UITextField) {
allCellsText.append(textField.text!)
print(allCellsText)
}
}
You cant have duplicate UITableViewDataSource and UITableViewDelegate methods it won't work like that.
Also in your code you have not set the delegate for the textField in both the cellForRowAtIndexPath() methods.
If you want to have two table view in a single controller try the following
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAtIndexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! Driver_Navigation_TableViewCell
if tableView == self.tableView1 {
cell.adressLabel.text = passengersAdress[indexPath.row]
cell.nameLabel.text = passengersName[indexPath.row]
cell.orderTextField.placeholder = "\(indexPath.row + 1)."
}
else {
cell.orderTextField.tag = indexPath.row
}
cell.orderTextField.delegate = self
return(cell)
}
Create outlets for the Table View
I want to be able to use a UIStepper that is located in each tableview row. I would like each UIStepper to update a label in the same tableview row that the UIStepper is in.
The code that I am trying is as follows
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cartListCell = tableView.dequeueReusableCell(withIdentifier: reuseCartListingCellIdentifier, for: indexPath) as? CartListingItemTableViewCell
cartListCell?.UI_STEPPER_NAME.value = VALUE_FROM_ARRAY
return cartListCell!
}
#IBAction func CartStoreQtyStepperAction(_ sender: UIStepper) {
// I need to update the value of a label in the tableview cell that tapped
}
UPDATED IMPLEMENTATION
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cartListCell?.CartListingProductQtyStepperOutlet.tag = indexPath.row
cartListCell?.CartListingProductQtyStepperOutlet.addTarget(self, action: #selector(self.CartStoreQtyStepperAction(_:)), for: UIControlEvents.valueChanged)
cartListCell?.tag = Int(CartStoreItemIdArray [indexPath.row])!
return cartListCell!
}
#IBAction func CartStoreQtyStepperAction(_ sender: UIStepper)
{
let stepperValue = Int(sender.value)
let indexPath = IndexPath(row: stepperValue, section: 0)
print(stepperValue)
if let cell = yourTableView.cellForRow(at: indexPath) as? CartListingItemTableViewCell
{
print(cell?.tag)
}
}
I am not able to access the tableview cell and the label in that when I am doing it like this. Can someone guide me how to do this?
// Custom tableview cell code
class CartListingItemTableViewCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
#IBOutlet weak var stepper: UIStepper!
}
// your view controller code (tableview data source)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cartListCell = tableView.dequeueReusableCell(withIdentifier: reuseCartListingCellIdentifier, for: indexPath) as? CartListingItemTableViewCell
let stepperValue = yourArray[indexPath.row]
cartListCell?.label.text = String(stepperValue) ?? "0"
cartListCell?.stepper.value = Int(stepperValue) ?? 0
cartListCell?.stepper.tag = indexPath.row
cartListCell?.stepper.addTarget(self, action: #selector(self.stepperValueChanged(_:)), for: UIControlEvents.valueChanged)
return cartListCell!
}
// handle stepper value change action
#IBAction func stepperValueChanged(_ stepper: UIStepper) {
let stepperValue = Int(stepper.value)
print(stepperValue) // prints value
let indexPath = IndexPath(row: stepperValue, section: 0)
if let cell = yourTableView.cellForRow(at: indexPath) as? CartListingItemTableViewCell {
cell.label.text = String(stepperValue)
yourValueArray[index] = stepperValue
}
}
This is a solution for Swift 4 using NSKeyValueObservation
First of all you need a property in your data model to keep the current value of the stepper.
var counter = 0.0
In the custom cell create outlets for the stepper and the label and a property for the observation. Connect the outlets.
#IBOutlet weak var stepper : UIStepper!
#IBOutlet weak var label : UILabel!
var observation : NSKeyValueObservation?
In the controller in cellForRow add the observer, dataSourceArray is the data source array.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cartListCell = tableView.dequeueReusableCell(withIdentifier: reuseCartListingCellIdentifier, for: indexPath) as! CartListingItemTableViewCell
let item = dataSourceArray[indexPath.row]
cartListCell.stepper.value = item.counter
cartListCell.label.text = "\(item.counter)"
cartListCell.observation = cell.stepper.observe(\.value, options: [.new]) { (_, change) in
cartListCell.label.text = "\(change.newValue!)"
item.counter = change.newValue!
}
return cartListCell
}
Solution for Swift 4 swift4.
Add these lines in cellforRowAt.
cartListCell.UI_STEPPER_NAME.tag = indexPath.row
cartListCell.UI_STEPPER_NAME.addTarget(self, action: #selector(CartStoreQtyStepperAction), for: .touchUpInside)
To access cell items in the stepper function use this line.
let cartListCell = sender.superview?.superview as! CartListingItemTableViewCell
//check whether your superview is table cell.
//then use the cell instance to update the cell label
Use sender.tag in the function to access particular cell for example.
cartListCell.UI_STEPPER_NAME.text = "\(VALUE_FROM_ARRAY[sender.tag])"
//sender.tag is similar to indexPath.row as we pass the tag value in the cellforRowAt.
//It will work then please accept my answer๐๐ผ๐.
Assign tag value to sender of UIStepper in cellForRowAt and access the change of UIStepper value in CartStoreQtyStepperAction action using same tag.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cartListCell = tableView.dequeueReusableCell(withIdentifier: reuseCartListingCellIdentifier, for: indexPath) as? CartListingItemTableViewCell
//Assign tag value
cartListCell?.UI_STEPPER_NAME.tag = indexPath.row
cartListCell?.UI_STEPPER_NAME.value = VALUE_FROM_ARRAY
cartListCell?.stepper.addTarget(self, action: #selector(self.CartStoreQtyStepperAction(_:)), for: UIControlEvents.valueChanged)
return cartListCell!
}
#IBAction func CartStoreQtyStepperAction(_ sender: UIStepper) {
let index = sender.tag
yourValueArray[index] = sender.value
//Reload particular cell for tableView
}
So I have a CollectionView inside my normal ViewController and if I select a cell and enter something in my textField and press the save Button it should update the nameLabel but I don't know how I can do that. Does anybody have a solution for this?
This is my current code:
private let reuseIdentifier: String = "Item"
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UIGestureRecognizerDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
ItemCollection.delegate = self
}
func textEnter() {
var text = textField.text
let indexPath = NSIndexPath()
let cell = ItemCollection.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! ItemCell
cell.nameLabel.text = text
}
#IBAction func save(sender: AnyObject, cell: UICollectionViewCell) {
textEnter()
}
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var ItemCollection: UICollectionView!
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! ItemCell
cell.backgroundColor = UIColor(red: 0/256, green: 128/256, blue: 255/256, alpha: 0.66)
cell.nameLabel.text = "Test1"
return cell
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
}
My Cell:
class ItemCell: UICollectionViewCell {
#IBOutlet weak var nameLbl: UILabel!
}
There are a couple of things you'll need to do to make this work.
First, you need to preserve context of the clicked cell by storing a "reference" to it. The easiest thing would be to introduce three new variables - section and item. Using these values you'll be able to re-create index path object and reference the cell.
Second, instead of calling dequeueReusableCellWithReuseIdentifier(), you should construct the new Index Path object using the values saved in the previous step and call cellForItemAtIndexPath(_ indexPath: NSIndexPath). This may return a nil if the cell is not in view.
Note: this will set the label only when the user taps Save and the text will most likely be lost if the cell scrolls out of view and then back. In order to preserve this, you should store the entered value in a variable (or refer to the textbox directly) when creating the cell in cellForItemAtIndexPath method.
Modified code example (some of the syntax may be off, I'm doing this from memory):
private let reuseIdentifier: String = "Item"
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UIGestureRecognizerDelegate, UITextFieldDelegate {
override func viewDidLoad() {
super.viewDidLoad()
ItemCollection.delegate = self
}
// Store the clicked item location
private section: Int = 0
private item: Int = 0
func textEnter() {
var text = textField.text
let indexPath = NSIndexPath(forItem: item inSection:section)
let cell = ItemCollection.cellForItemAtIndexPath(indexPath) as! ItemCell
cell.nameLabel.text = text
}
#IBAction func save(sender: AnyObject, cell: UICollectionViewCell) {
textEnter()
}
#IBOutlet weak var textField: UITextField!
#IBOutlet weak var ItemCollection: UICollectionView!
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! ItemCell
cell.backgroundColor = UIColor(red: 0/256, green: 128/256, blue: 255/256, alpha: 0.66)
cell.nameLabel.text = "Test1"
return cell
}
// Need to capture the tapped cell in here
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
section = indexPath.section
item = indexPath.item
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
}