UITableview Update Single Cell - ios

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!

Related

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 JSON id with button in tableview cell in swift4

I'm using tableView cell with button name of delete and I want to delete my JSON data from tableView cell I need to so That JSON ID to delete the JSON data from my Tableview. I'm using to get the JSON ID for that indexPath in didSelect row function. but the problem is I can't get JSON Id from that indexPath without pressing the row.
var selectedList: JSONList?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedList = JSONList[indexPath.row]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableviewCell
cell?.Numbers.text = JSONList[indexPath.row].Number
cell?.Trash.addTarget(self, action: #selector(Deleting), for: .touchUpInside)
cell?.selectionStyle = .none
return cell!
}
//here is my delete function calling
func Deleting() {
let selectedListObj = selectedList
print(selectedListObj!.id)
}
First of all set a tag to the button which it will be in your case the
row of the IndexPath, add this code in cellForRowAt:
cell?.Trash.addTarget(self, action: #selector(deleting(_:)), for:
.touchUpInside)
cell?.Trash.tag = indexPath.row
You need to modify the deleting() function to be #objc since selectors
after swift 3+ are a must to add that and add the button as param into it:
#objc private func deleting(_ button:UIButton){
// here you got the object
let selectedObject = JSONList[button.tag]
}
use closure to get the tapped buttons cells indexpath.
update the table view cell with ibaction and call the closure whenever tapping on trash button.
class FakeTableCell: UITableViewCell{
var selectedCell: ((UITableViewCell) -> ())?
#IBAction func trashButtonTapped(){
selectedCell?(self)
}
}
then we can get the indexpath of the cell in cell for row for indexpath as follows
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = FakeTableCell()
cell.selectedCell = { selectedCell in
if let ip = tableView.indexPathForRow(at: selectedCell.center){
self.deleteData(with: ip.row)
}
}
return cell
}
func deleteData(with row: Int){
// get the object by JSONList[row]
let item = JSONList[row]
// perform deletion
}

swift ios trigger a table index when button is pressed

I have a button in my table and when it was click it will trigger the selected table index. for example when i click the button at a selected index it will trigger the code below. Thank You
code
func indexWasPressed(cell: ToDoListTableViewCell) {
trigger it here
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You selected cell #\(indexPath.item)!")
Use below code to get index from TableViewCell:
func indexWasPressed(cell: ToDoListTableViewCell) {
let indexPath = yourTableView.indexPath(for: cell)
let index = indexPath.row
}
try this:-
#objc func buttonPressed(sender: UIButton) {
// printing selected row
print("index path: \(sender.tag)")
//do you want section try this
var section = Int()
let pointInTable: CGPoint = sender.convert(sender.bounds.origin, to: self.tblView)
let cellIndexPath = self.tblView.indexPathForRow(at: pointInTable)
section(cellIndexPath!)
section = cellIndexPath!.row
print(section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("cell for rows in \(indexPath.section)")
let cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifire", for: indexPath) as! yourcell
cell.yourbutton.tag = indexpath.row
cell.yourbutton.addTarget(self, action: #selector(buttonpressed(sender:)), for: UIControlEvents.touchUpInside)
}

Swift UitableViewcell with button

I have a strange behavior in the UITABLEVIEW.
I have a TableView with Button, what I wanted to do is when I clicked to a button in the tableView, I want the color border to change to red.
The problem is that the color is changing not only for the clicked button, but also for others row in the tableview:
Here is my implementation
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! chooseProductTVC
cell.Btn_AddProduct.addTarget(self, action: #selector(self.btnAction(_:)), for: .touchUpInside)
return cell
}
#objc func btnAction(_ sender: MyButton) {
sender.BorderColor = UIColor.red
let point = sender.convert(CGPoint.zero, to: tbl_View_ChooseProduct as UIView)
let indexPath: IndexPath! = self.tbl_View_ChooseProduct.indexPathForRow(at: point)
let object = self.fetchedResultsController.object(at: indexPath)
print (object.produit_name)
print("row is = \(indexPath.row) && section is = \(indexPath.section)")
}
As you can see in the picture below I have only clicked on the first button (Abricot) ==> other button has also automatically changed the border (Avocat) and many others.
This is because of cell dequeuing try to re set when you load the table , suppose here default is blue
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")
cell.Btn_AddProduct.addTarget(self, action: #selector(self.btnAction(_:)), for: .touchUpInside)
if selected
cell.Btn_AddProduct.BorderColor = UIColor.red
else
cell.Btn_AddProduct.BorderColor = UIColor.blue
return cell
}
Since we reuse the cell for displaying, we have to modify the color every time after dequeing.
Add new property selected to product model.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! chooseProductTVC
cell.Btn_AddProduct.addTarget(self, action: #selector(self.btnAction(_:)), for: .touchUpInside)
let object = self.resultArray(at: indexPath.row)
if object.selected
cell.Btn_AddProduct.BorderColor = .red
else
cell.Btn_AddProduct.BorderColor = .blue
return cell
}
#objc func btnAction(_ sender: MyButton) {
sender.BorderColor = UIColor.red
let point = sender.convert(CGPoint.zero, to: tbl_View_ChooseProduct as UIView)
let indexPath: IndexPath! = self.tbl_View_ChooseProduct.indexPathForRow(at: point)
let object = self.fetchedResultsController.object(at: indexPath)
object.selected = true
print (object.produit_name)
print("row is = \(indexPath.row) && section is = \(indexPath.section)")
}
Tip: You could use tableChooseProduct(iOS) instead of tbl_View_ChooseProduct.(android). This link may helpful.
You have to store default Colors in Dictionary.
var orderColor = [Int : UIColor]()
override func viewDidLoad() {
super.viewDidLoad()
for i in 0...29 // TotaL number of rows
{
orderColor[i] = UIColor.blue // DEFAULT color of the Button
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 30
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! chooseProductTVC
cell.Btn_AddProduct.addTarget(self, action: #selector(self.btnAction(_:)), for: .touchUpInside)
cell.Btn_AddProduct.BorderColor = orderColor[indexPath.row]
cell.selectionStyle = .none
return cell
}
#objc func btnAction(_ sender: MyButton) {
let point = sender.convert(CGPoint.zero, to: tbl_View_ChooseProduct as UIView)
let indexPath: IndexPath! = self.tbl_View_ChooseProduct.indexPathForRow(at: point)
orderColor[indexPath.row] = UIColor.red
tbl_View_ChooseProduct.reloadData()
}

UITableviewCell toggling only one custom checkmark state not working - Swift 3

I have reviewed this link and the solutions were not working for me.
I am trying to select one row and when I do select it, it adds a checkmark to the label. If another row is selected while there is an existing checkmark, it will uncheck the previous one which is stored in a selectedIndexPath variable.
It works in the beginning, however when scrolling through the tableview several times, I occasionally see a cell selected that should not be as indicated in this image:
What I am doing when a user selects a cell:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? CustomCell {
let customCell = customCellData[indexPath.row]
customCell.toggleSelected()
cell.configureCheckmark(with: customCell)
}
if let oldIndexPath = selectedIndexPath, let cell = tableView.cellForRow(at: oldIndexPath) as? CustomCell, oldIndexPath.row != indexPath.row {
let customCell = customCellData[oldIndexPath.row]
customCell.toggleSelected()
cell.configureCheckmark(with: customCell)
}
if let selected = selectedIndexPath, selected.row == indexPath.row {
selectedIndexPath = nil
tableView.deselectRow(at: indexPath, animated: true)
} else {
selectedIndexPath = indexPath
}
}
and in for cellForRowAt: (is it redundant to check the selectedIndexPath and the state in the model?)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! CustomCell
let customCell = customCellData[indexPath.row]
cell.customCell = customCell
if selectedIndexPath == indexPath {
cell.checkLabel.text = "✔️"
} else {
cell.checkLabel.text = ""
}
return cell
}
and finally in the CustomCell:
var customCell: CustomCell? {
didSet {
if let customCell = customCell {
configureCheckmark(with: customCell)
}
}
}
func configureCheckmark(with customCell: CustomCellData) {
if customCell.isSelected {
checkLabel.text = "✔️"
} else {
checkLabel.text = ""
}
}
In CustomCellData I toggle the state as follows:
class CustomCellData {
var isSelected = false
func toggleSelected() {
isSelected = !isSelected
}
}
I am scratching my head on this and unsure what to do, any help would be great.
The easiest solution is to reduce didSelectRowAt to
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
selectedIndexPath = indexPath
tableView.reloadData()
}
This updates all visible cells properly.
Or more sophisticated version which updates only the affected rows and checks if the cell is already selected
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
if selectedIndexPath != indexPath {
let indexPathsToReload = selectedIndexPath == nil ? [indexPath] : [selectedIndexPath!, indexPath]
selectedIndexPath = indexPath
tableView.reloadRows(at: indexPathsToReload, with: .none)
} else {
selectedIndexPath = nil
tableView.reloadRows(at: [indexPath], with: .none)
}
}
The code in cellForRowAt does the rest.

Resources