Increment Label Value with UIStepper in UITableViewCell - ios

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
}

Related

UITableview Update Single Cell

I got a play button on my tableview custom cell, whenever I tapped the play button. I want the selected button image to change to pause image.
The issue is that all the other buttons images are getting updated.
So all the images are changing to the pause image, instead of the selected button.
I tried to get the indexpath of the button tapped and reload only that row, but that doesn't seem to make a difference.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let playerCell = tableView.dequeueReusableCell(withIdentifier: "playerCell", for: indexPath) as! PlayerCell
let item = subcategory?.items?[indexPath.row]
// Tap gestures extension for cell button action
playerCell.playPause.addTapGestureRecognizer {
AudioController.shared.setupPlayer(item: item)
if let selectedCell = tableView.cellForRow(at: indexPath) as? PlayerCell {
selectedCell.playPause.setImage(#imageLiteral(resourceName: "pause"), for: .normal)
tableView.reloadRows(at: [IndexPath(row: indexPath.row, section: 1)], with: .none)
}
print("index \(indexPath)")
}
What you can do is add a tag to the button. So within the method that you create the cells override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell, you add a tag to the button within that cells that represents the indexPath. Then from within the selector that you assign the button, you can get the cell that you want to alter.
For example:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "yourCell", for: indexPath)
cell.button.tag = indePath.row
cell.button.addTarget(self, action: #selector(yourSelector(_sender:)), for: .touchUpInside)
}
func yourSelector(_sender: UIButton){
let cell = tableView.cellForRow(at: IndexPath(row: sender.tag, section: 0)) as! YourCellType
// Change the image, play/pause audio for that cell
}
When you tap on the button in cell, tableView(_:didSelectRowAt:) won't be trigger, so I suggest using delegate to detect button action.
And you need to keep tracking the change of the cell's button status.
For example:
PlayCell.swift
protocol PlayCellDelegate: class {
func playCellPlayButtonDidPress(at indexPath: IndexPath)
}
class PlayerCell: UITableViewCell {
let playButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(playButtonPressed(_:)), for: .touchUpInside)
return button
}()
weak var delegate: PlayCellDelegate?
var item: MyItem? {
didSet {
if item?.status == .paused {
// set pause image for playButton here
} else if item?.status == .playing {
// set play image for playButton here
}
}
}
var indexPath: IndexPath?
#objc func playButtonPressed(_ sender: UIButton) {
guard let indexPath = self.indexPath else { return }
delegate?.playCellPlayButtonDidPress(at: indexPath)
}
}
Model.swift
struct Subcategory {
// ...
var items: [MyItem]?
}
struct MyItem {
// ...
var status: Status.stop
enum Status {
case playing, paused, stopped // etc..
}
}
TableViewController.swift
class TableViewController: UITableViewController, PlayCellDelegate {
private var subcategory: Subcategory?
private let cellId = "Cell"
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as? PlayerCell {
cell.delegate = self
cell.item = subcategory?.items?[indexPath.row]
cell.indexPath = indexPath
return cell
}
return UITableViewCell()
}
func playCellPlayButtonDidPress(at indexPath: IndexPath) {
// you only need to change model here, and reloadRows will update the cell.
if subcategory?.items?[indexPath.row].status == .play {
subcategory?.items?[indexPath.row].status = .pause
} // other logic..
tableView.reloadRows(at: [indexPath], with: .none)
}
}
Hope it helps!

Reading data from 2 textfields for each TableViewCell

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)
}
}

Access button index from TableView inside another TableView

I have a TableView inside Another TableView, and the inner tableView contains buttons. I am able fire a method when click on button, but not able to get the indexPath. The app is getting crashed when I use the below code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell") as! MyCell
cell.myButton?.addTarget(self, action:#selector(myButtonPressed(_:)), for:.touchUpInside)
return cell
}
#objc func myButtonButtonPressed(_ sender: AnyObject) {
let button = sender as? UIButton
let cell = button?.superview?.superview as? UITableViewCell
let clasObj = myCell()
let indexPath = myCell.InsidetabView.indexPath(for: cell!)
let index = (indexPath?.row)!
print(index)
}
I gave tag to the button on TableView, cellForRowAt indexPath Method and accessed it on buttonPressed method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell") as! MyCell
cell.myButton?.addTarget(self, action:#selector(myButtonPressed(_:)), for:.touchUpInside)
cell.button.tag = indexPath.row
return cell
}
#objc func myButtonButtonPressed(_ sender: AnyObject) {
let button = sender as? UIButton
let row = button!.tag
}

How to get data from the multiple selected rows and show in another VC tableview cell Swift4

Hello I am trying to complete my task since 2 days but still no success searched everywhere , I want to select multiple rows from tableview cells (which contains 2 labels and one image) then I want to transfer into another vc and show in tableview , I am able to select multiple rows and get this type index from selected rows but now I don't know how to get data and transfer into another vc and show in table view please help I am getting selected rows index like this [[0, 1], [0, 2], [0, 3]
VC Code
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
#IBOutlet weak var tableview: UITableView!
var labone = ["1","2","3","4","5","6"]
var labtwo = ["a","b","c","d","e","f"]
var img = ["bag","bag","bag","bag","bag","bag"]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func button(_ sender: Any) {
let selectedindexPath = tableview.indexPathsForSelectedRows
if(selectedindexPath != nil){
let toy = labone[0]
print(toy)
print(selectedindexPath) }
else{
print("null")
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return labone.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let name = cell.viewWithTag(1) as! UILabel
let name_two = cell.viewWithTag(2) as! UILabel
let imgg = cell.viewWithTag(3) as! UIImageView
name.text = labone[indexPath.row]
name_two.text = labtwo[indexPath.row]
imgg.image = UIImage(named: img[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath)
cell.contentView.backgroundColor = UIColor.yellow
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
// let selectedCell = tableview.cellForRow(at: indexPath)
if let label = cell?.contentView.viewWithTag(4) as? UIImageView {
label.image = UIImage(named: "check_disable")
}
}
}
You should use some structure like below and then update the state of item selected or deselected then when you want to go to next viewController you can filter your dataSource with the items selected like below,
class CellModel {
var labelOne: String
var labelTwo: String
var imageName: String
var isSelected = false
init(_ labelOne: String, labelTwo: String, imageName: String) {
self.labelOne = labelOne
self.labelTwo = labelTwo
self.imageName = imageName
}
}
Update your viewController,
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
let dataSource = [CellModel("1", labelTwo: "a", imageName: "bag"),
CellModel("2", labelTwo: "b", imageName: "bag"),
CellModel("3", labelTwo: "c", imageName: "bag"),
CellModel("4", labelTwo: "d", imageName: "bag"),
CellModel("5", labelTwo: "e", imageName: "bag"),
CellModel("6", labelTwo: "f", imageName: "bag")]
}
then you can update your cellForRowAt like below,
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
let name = cell.viewWithTag(1) as! UILabel
let name_two = cell.viewWithTag(2) as! UILabel
let imgg = cell.viewWithTag(3) as! UIImageView
let model = self.dataSource[indexPath.row]
name.text = model.labelOne
name_two.text = model.labelTwo
imgg.image = UIImage(named: model.imageName)
return cell
}
update the numberOfRowInSection with this
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.dataSource.count
}
and you can update the state of model when cell is selected/deseleced like this,
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell",for: indexPath)
cell.contentView.backgroundColor = UIColor.yellow
self.dataSource[indexPath.row].isSelected = true
}
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
// let selectedCell = tableview.cellForRow(at: indexPath)
if let label = cell?.contentView.viewWithTag(4) as? UIImageView {
label.image = UIImage(named: "check_disable")
self.dataSource[indexPath.row].isSelected = false
}
}
and finally in button action you can filter the selected models and pass to next viewController
#IBAction func button(_ sender: Any) {
let selectedItems = self.dataSource.filter({ $0.isSelected == true })
// Pass to next ViewController the selectedItmes
}
As #Rakshith suggest in their comment, move your data (labone, labtwo, img arrays) out of the view controller and into a model object. Pass that model object to your second view controller.
Also create an array selectedIndexPaths in your second view controller. When you tap the button in your ViewController, get the array of selected index paths and pass it to your second view controller.
In your second view controller's viewWillAppear, use the selectedIndexPaths variable to copy the items selected items into an array itemsToDisplay and use that in your table view data source methods to populate the second view controller's table view.

Adding UIButton to detailTextLabel/right side of UITableView

I'm quite new to Swift and I would like to know what is the easiest and simplest way for me to add in 10 buttons to 10 of my cell rows in my TableViewController.
P.S: It would be nice if the 10 buttons perform differently instead of duplicate.
import UIKit
import FirebaseDatabase
var ref: DatabaseReference?
var databaseHandle: DatabaseHandle?
var postData = [String]()
class TableViewController5: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference() //set the firebase reference
// Retrieve the post and listen for changes
databaseHandle = ref?.child("Posts3").observe(.value, with: { (snapshot) in
postData.removeAll()
for child in snapshot.children {
let snap = child as! DataSnapshot
let key = snap.key
postData.append(key)
}
self.tableView.reloadData()
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return postData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.font = UIFont.init(name: "Helvetica", size: 23)
cell.textLabel?.text = postData[indexPath.row]
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
return 80
}
}
From what I can understand, you should use prototype cell and add an IBAction in the cell and in that cell you should perform the segue and pass whatever data you need to customise the page you load.
In your tableView(_:cellForRowAt:) method, you can set a tag for the button.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? prototypeCell
cell.voteButton.tag = indexPath.row
return cell
}
Then in your cell's class, in the IBAction I mentioned earlier, check for the tag and set the data to pass accordingly.
Then in your prepareForSegue:sender: method, just pass the data you want to pass to the next view controller and all should work fine.
Check if it helps You
//Common cell Variable
var cell = UITableViewCell()
//UIButton commonly Declared
var acceptRequest = UIButton()
var declineRequest = UIButton()
My TableView Cell contains two button as shown Accept & Decline
In my tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) I added Handlers for button
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Cell for dequeuing
cell = self.RequestListTableView.dequeueReusableCell(withIdentifier: "Cell")!
//Used Tags here
//You can Make use of TableViewCell class here and Access Buttons from there
acceptRequest = cell.viewWithTag(4) as! UIButton
//Adding action Handlers for UIbuttons
self.acceptRequest.addTarget(self, action: #selector(RequestListView.AcceptRequest(sender:)), for: .touchUpInside)
return cell
}
my Button handler
func AcceptRequest (sender: UIButton) {
//Dequeue cell with identifier can be ignored
cell = self.RequestListTableView.dequeueReusableCell(withIdentifier: "Cell")!
//Get button position from TableView : Required
let buttonPosition : CGPoint = sender.convert(CGPoint.zero, to: self.RequestListTableView)
//Get index Path of selected Cell from TableView
//Returns Table Cell index same as didSelect
let indexPath : IndexPath = self.RequestListTableView.indexPathForRow(at: buttonPosition)!
//Can be used if you need to update a view
cell = self.RequestListTableView.cellForRow(at: indexPath)!
//Now time to access using index Path
cell.textLabel.text = array[indexPath.row] //Example
}
Can use this Procedure for even single Button or for multiple buttons too
In case if you have more than one button in one cell, this will help you

Resources