How do I reference a UITableViewCell in a UITableView - ios

The table view and cells have been created and populated with a label and slider.I have an IBAction on the slider when the value is changed.
How do I get that value back into the label in the cell?
I have an IBOutlet linked in the table view.
#IBAction func slideChange(_ sender: UISlider) {
let currentValue = sender.value // gets slider's value
let row = sender.tag // gets slider's row in table
myTableView.cell.myLabel.text = sender.value // Here i need to show updated value
print("Slider in row \(row) has a value of \(currentValue)") // works
}

Try this:
#IBAction func slideChange(_ sender: UISlider){
let cell: UITableViewCell? = (sender.superview?.superview as? UITableViewCell) // track cell using your view hierarchy and this your cell
cell.myLabel.text = sender.value
}
OR
let rootViewPoint: CGPoint? = sender.superview?.convert(sender.center, to: tblView)
var indexPath: IndexPath? = tblView?.indexPathForRow(at: rootViewPoint!)
let cell: UITableViewCell? = tblView?.cellForRow(at: indexPath!)

Get a reference to the table view cell
let cell = tableView.cellForRow(at: IndexPath(row: sender.tag, section: 0))
Update its label
cell?.myLabel.text = sender.value
Note: This method returns nil when cell is not visible. In your case this won't be an issue.

Related

How to access textview value which is inside tableview cell in swift 5?

I have one viewcontroller, inside that I have one table view and 2 buttons save and cancel.
In tableview cell i have one textview. after adding some text in textview i want to show that text. I am not sure how to get that tableview text on save button click. (number of rows may be dynamic).
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableview.dequeueReusableCell(withIdentifier: "SummaryManualEditContentCell", for: indexPath) as? SummaryManualEditTableCell {
cell.txtAnswers.text = "enter text here"
return cell
}
return UITableViewCell()
}
#IBAction func btnSave(_ sender: Any) {
print("textviewText1 + textviewText2 + and so on ")
}
In Addition to this on button click i want to add all that text multiple textviews into one string.
is there any clean and best way to achieve this?
Thank you for helping!
You need to get the indexPath of the cell whose text you want to get
get the cell for that indexpath like
#IBAction func btnSave(_ sender: Any) {
let indexPath = IndexPath(row: 0, section: 0)
if let cell = tableView.cellForRow(at: indexPath) as? SummaryManualEditTableCell {
let text = cell.txtAnswers.text
}
}
And if you have multiple cells with textFields you can loop around to get all fields
#IBAction func btnSave(_ sender: Any) {
var allTextViewsText = ""
for i in 0...5{
let indexPath = IndexPath(row: i, section: 0)
if let cell = tableView.cellForRow(at: indexPath) as? SummaryManualEditTableCell {
allTextViewsText += cell.txtAnswers.text
}
}
print(allTextViewsText)
}
But keep it in mind that this approach only works in the case of visible cells otherwise for non visible cells you will get nil
I will suggest you to implement textView:shouldChange in each cell who has textView with a delegate to the tableView's viewController. When text is changed in the cell, delegate should propagate the change to the viewController which will save the value in a variable.
Then, when you press on the save button, you would simply take values from variables.

Accessing subviews from UITapGestureRecognizer

I have a collection view with cells that are populated based on an array. Each cell has a UIImageView and an UILabel. I want to be able to update the text within the UILabel each time the Cell View itself is tapped. I have the tapping gesture working fine and i can print out the sender information and access the sender view and even get the restoration identifier but I can't seem to access the 'myImage' or 'myLabel' sub views within that cell which is being tapped.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! CardCell
cell.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tap)))
let railroadName = railroadNames[indexPath.item]
cell.myImage.image = #imageLiteral(resourceName:railroadName)
cell.myImage.clipsToBounds = true
cell.myImage.layer.cornerRadius = 8
cell.restorationIdentifier = railroadName
cell.myLabel.isHidden = true
cell.myLabel.text = "2"
cell.myLabel.layer.backgroundColor = UIColor(red:0.25, green:0.52, blue:0.96, alpha:1.0).cgColor
cell.myLabel.layer.cornerRadius = 18
cell.myLabel.textColor = UIColor.white
return cell
}
#objc func tap(_ sender: UITapGestureRecognizer) {
//print(sender)
//print(sender.view?.restorationIdentifier)
}
Can't access subviews. Want to take the current text from 'myLabel' and increase the count by 1 each tap.
You can use below code to get label text of cell. but i insist you to manage this in railroadNames array. so, you can save label text in array object's field and can get that field value using indexpath using below code and perform operation on based of your requirement. and then you can reload that particular cell to show effect on tableview.
Also, you should use didSelectItemAt method to get select cell event. if not necessary reason to use UITapGestureRecognizer.
#objc func tap(_ sender: UITapGestureRecognizer) {
//Get Collection view cell indexpath on based of location of tap gesture
let cellPosition = sender.location(in: self.collectionView)
guard let indexPath = self.collectionView.indexPathForItem(at: cellPosition) else{
print("Indexpath not found")
return
}
//Get Ceollection view cell
guard let cell = self.collectionView.cellForItem(at: indexPath) as? CellMainCategoryList else{
print("Could't found cell")
return
}
//Get String of label of collection view cell
let strMyLable = cell. myLabel.text
//you can process addition on based of your requirement here by getting Int from above string.
}
instead of above, you can manage it like below code also:
#objc func tap(_ sender: UITapGestureRecognizer) {
let cellPosition = sender.location(in: self.collectionViewCategaries)
guard let indexPath = self.collectionViewCategaries.indexPathForItem(at: cellPosition) else{
print("Indexpath not found")
return
}
let railroadName = railroadNames[indexPath.item]
let myLableText = railroadName.myLabelValue
//update value
railroadName.myLabelValue = "2"
self.collectionView.reloadItems(at: [indexPath])
}
Hope this helps.

Multiple buttons in UITableViewCell in a 'top-down' UITableView

I am trying to work out how to action buttons within a UITableViewCell when the table view is 'top down' - as in, each new row is added to the top of the table view. I achieve this with the following code:
I use the following code to insert new items into my model array and then into the tableview:
let newItem = Item(text: inputTextView.text, time: Date())
items.insert(newItem, at: 0) //inserting into the front of model array
tableView.beginUpdates()
let indexPath:IndexPath = IndexPath(row:0, section:0)
tableView.insertRows(at: [indexPath], with: .fade)
tableView.endUpdates()
Within the cellForRowAt function I run the following code:
let cell = tableView.dequeueReusableCell(withIdentifier: postCellID, for: indexPath) as! NewPostCell
let item = items[indexPath.row]
cell.postTextLabel.text = text
cell.timeLabel.text = dateFormatter.string(from: time)
cell.selectionStyle = .none
return cell
Each of my tableview cells have three buttons in it.
How do I connect up these buttons so I know which button is pressed from which indexPath?
The problem is that if I use indexPath.row to tag the buttons, then the buttons in all cells gets tagged with 0, as each insert is happening at the top of the table at indexPath.row 0th position.
I thought of tagging the buttons with the current size of my model array, but that doesn't work either as when cells are re-used they could then be tagged with the length of the array at that point, which would be wrong.
There are a lot of apps that have 'last entry at the top' of the tableview sort of setup, with buttons in cells. So there must be a way to do this.
You can add UIButton to your UITableViewCell and access these UIButton via tag and add target method to these buttons as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableVw.dequeueReusableCell(withIdentifier: "Cell") as UITableViewCell!
//Access UIButton
let button1:UIButton = cell.viewWithTag(10) as! UIButton
let button2:UIButton = cell.viewWithTag(11) as! UIButton
let button3:UIButton = cell.viewWithTag(12) as! UIButton
//Add Action Methods to UIButtons
button1.addTarget(self, action: #selector(FisrtButtonClick), for: .touchUpInside)
button2.addTarget(self, action: #selector(SecondButtonClick), for: .touchUpInside)
button3.addTarget(self, action: #selector(ThirdButtonClick), for: .touchUpInside)
return cell
}
Button Actions
// MARK: - UIButton Methods.
func FisrtButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
func SecondButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
func ThirdButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
Main idea
These are the basic steps you have to do:
Set unique tag values for each of the 3 button types: this will allow to identify which of the 3 buttons of a cell is pressed. (for instance tag value 1 for the first of your buttons, value 2 for the second kind, value 3 for the third button).
Link the 3 buttons to a #IBAction method.
You can also do this programmatically with target-actions: call aButton.addTarget(target:, action:, for:) for each button.
Then when a button will be pressed, you will use an helper function to determine the cell index of the button that was pressed.
Code
The #IBAction method should look like #IBAction func buttonTrigerred(_ sender: UIButton){...} and the code would be:
#IBAction func buttonTrigerred(_ sender: UIButton){
// -- retrieving the index path of the cell, as #NikhleshBagdiya posted
// Determine the indexPath of the cell
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableView)
let cellIndexPath = tableView.indexPathForRow(at: buttonPosition)
// --
// Determine which button is called
if sender.tag == 1 { ... } // the first kind of button has been pressed
else if sender.tag == 2 { ... } // second button kind
else if sender.tag == 3 { ... } // third button kind
}
Setting the tag value
Each of my tableview cells have three buttons in it.
I suppose you have designed your UITableViewCell in a Storyboard or Xib file. So for each of the 3 buttons of your cell: select the button, go to the attributes inspector, set the tag to the custom values indicated above.

Text is replacing while scrolling tableview

I need to increment decrement label value of a particular row. I am using following method, it is working but when I scroll the table view then values are replacing with other's row.
Like I am showing two row on a screen according to design so while scrolling, first row's label value is replacing with forth row label and so on.
#IBAction func plusBtnAction(_ sender: UIButton) {
var cell:TableViewCell = self.tebleView.dequeueReusableCell(withIdentifier: "Cell")! as UITableViewCell as! TableViewCell
let indexPath = IndexPath(row: sender.tag, section: 0)
cell = (self.tebleView.cellForRow(at: indexPath) as? TableViewCell)!
if cell.tag == sender.tag {
print(sender.tag)
print(cell.countLbl.tag)
cell.countLbl.text = "\(Int(cell.countLbl.text!)! + 1)"
}
}
Remember, you are reusing the cells - dequeueReusableCell each time you need to display a new cell, so what's happening here is the first row disappears and when it is redisplayed, you are using the cell that was used for row 4.
The solution to this is to keep a track of your counter in an array outside of the tableView (table, not teble :-) ) Something like this...
var tableDataCounters : [Int] = []
// set up your data ...
cell.countLbl.text = "\(tableDataCounters[indexPath.row] + 1)"
By using dequeueReusableCell(withIdentifier: ) method of UITableView, you are initially creating cells and later on reusing those created cells. You are facing this problem due to this. Since you have only one row whose text you need to change, it's better to keep the text for that row stored externally. For the plusBtnAction, it's better to do the following:
var cellLabelTextArray: [String] = [] // populate this with data for each cell
#IBAction func plusBtnAction(_ sender: UIButton) {
var cell:TableViewCell = self.tebleView.dequeueReusableCell(withIdentifier: "Cell")! as UITableViewCell as! TableViewCell
let indexPath = IndexPath(row: sender.tag, section: 0)
var cellLabelText = cellLabelTextArray[indexPath.row]
cellLabelText = "\(Int(cellLabelText) + 1)"
cellLabelTextArray[indexPath.row] = cellLabelText
if let visiblePaths: [IndexPath] = self.tableView.indexPathsForVisibleRows {
if visiblePaths.contains(indexPath) {
self.tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
}
In the above code, we are checking if the indexPath is visible. If it is, then reload that particular indexPath. Otherwise, we don't need to.

How to get Text from a label in custom cell in Table View

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tbl_vw.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)as!cuscellTableViewCell
cell.txt_lbl.text = self.section[indexPath.row];
cell.sel_btn.addTarget(self, action: #selector(self.switchChanged), forControlEvents: .ValueChanged)
return cell
}
func switchChanged(sender: AnyObject) {
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
if switchControl.on {
print("The switch is on lets martch")
}
}
I have a switch and a label in a custom cell in tableview. When I ON the switch i need to get the text in the label, can any one help how to make it possible.
Use the tag property of UISwitch to store the position and use it in your handler to get the actual text form section array.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tbl_vw.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)as!cuscellTableViewCell
cell.txt_lbl.text = self.section[indexPath.row];
cell.sel_btn.tag = indexPath.row
cell.sel_btn.addTarget(self, action: #selector(self.switchChanged), forControlEvents: .ValueChanged)
return cell
}
func switchChanged(sender: AnyObject)
{
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
let text = self.section[switchControl.tag]
print(text)
if switchControl.on
{
print("The switch is on lets martch")
}
}
If you have multiple sections with multiple rows you can use a dictionary and store a tuple.
var items = [Int:(Int,Int)]()
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tbl_vw.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)as!cuscellTableViewCell
cell.txt_lbl.text = self.section[indexPath.row];
cell.sel_btn.tag = indexPath.row
let tuple = (section: indexPath.section, row: indexPath.row)
self.items[cell.sel_btn.hashValue] = tuple
cell.sel_btn.addTarget(self, action: #selector(self.switchChanged), forControlEvents: .ValueChanged)
return cell
}
func switchChanged(sender: AnyObject)
{
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
let tuple = self.items[switchControl.hashValue]
print(tuple.0) // this is the section
print(tuple.1) // this is the row
if switchControl.on
{
print("The switch is on lets martch")
}
}
In your case move func switchChanged(sender: AnyObject) to your custom class and assign selector in customCell for UISwitch. You will start receiving your switchChange event in your customCell now you are customCell you have access to your UISwitch & UILabel objects, also keep a boolean flag to maintain switch state if required.
Step 1: Move your switchChanged to customCell class.
Step 2: In customCell awakeFromNib or init method assign selector to UISwitch object to cell itself to receive switch change event in cell.
Step 3: Once your switch change event fires update your UILabel as you have access to it inside customCell.
Let me know if you have any doubt.
what you need to do is use block
from Get button click inside UI table view cell
first move your action method to table cell
in tableview cell add block property
#property (copy, nonatomic) void (^didTapButtonBlock)(id sender);
with your action method
call block
- (void)didTapButton:(id)sender {
if (self.didTapButtonBlock)
{
self.didTapButtonBlock(sender);
}
}
and receive that from cell , you have both section and row there
[cell setDidTapButtonBlock:^(id sender)
{
// Your code here
}];
Hope it helps
Set tag of sel_btn(UISwitch) in cellForRowAtIndexPath method.
sel_btn.tag = indexPath.row
On switchChanged method, get the tag of sel_btn and thus text of label.
let switchControl: UISwitch = sender as! UISwitch
let tag = switchControl.tag
let cell : cuscellTableViewCell = tbl_vw.cellForRowAtIndexPath(NSIndexPath(forRow: tag, inSection: 0)) as? cuscellTableViewCell
print(cell.txt_lbl.text)
If you want to get data from model not from view, then override the above two lines with below line:-
let textValue = self.section[tag];
In tableview delegate method -
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath)
add index path value to switch tag value -
cell.sel_btn.tag = indexPath.row
In Switch Value change method just get selected text when that switch is ON
func switchChanged(sender: AnyObject)
{
let switchControl: UISwitch = sender as! UISwitch
print("The switch is \(switchControl.on ? "ON" : "OFF")")
// If switch ON
if switchControl.on
{
print("The switch is ON”)
// Get Selected Label Text
let selectedText = self.section[switchControl.tag]
print(selectedText)
}
}

Resources