Reputation: 11
I use TableView with selected button, I have a problem with my code because when the button is selected, I don't know what is the IndexPath of my cell.
I use a file TableView cell with table view cell.xib
@IBOutlet weak var lbTitle: UILabel!
@IBOutlet weak var lbDetail: UILabel!
@IBOutlet weak var btnCheckMark: UIButton!
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
}
In my View controller I have this :
override func viewDidLoad() {
super.viewDidLoad()
self.topBar.rightBarButtonItem = UIBarButtonItem(title: "Apply", style: .done, target: self, action: #selector(self.apply))
self.topBar.leftBarButtonItem = UIBarButtonItem(image: UIImage(named: "backButton"), style: .done, target: self, action: #selector(backHome))
listeView.register(UINib.init(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "CheckList")
listeView.dataSource = self
listeView.delegate = self
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return deviceData.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let rowpath = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckList") as! TableViewCell
cell.lbTitle.text = "\(self.deviceData[indexPath.row].brandName) \(self.deviceData[indexPath.row].modelName)"
cell.lbDetail.text = "\(self.deviceData[indexPath.row].operatorName) \(self.deviceData[indexPath.row].version), \(self.deviceData[indexPath.row].browserName) \(self.deviceData[indexPath.row].version)"
//cell.selectionStyle = .none
cell.btnCheckMark.addTarget(self, action: #selector(checkMarkButton(sender:)), for: .touchUpInside)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
let test = TableViewCell()
test.lbTitle.text = "11"
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 71.0
}
@objc func checkMarkButton(sender: UIButton) {
if sender.isSelected {
sender.isSelected = false
nb -= 1
//rint(paramaters)
} else {
sender.isSelected = true
paramaters["devicesList[\(nb)][deviceId]"] = id
nb += 1
}
print(paramaters)
}
In my function checkMarkButton, I want to know the indexPath.row
Upvotes: 0
Views: 2449
Reputation: 31
This is not the right way to do that, Instead try to use the didSelectRowAt delegate function and update the data, like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
data[indexPath.row].selected = !data[indexPath.row].selected
tableView.reloadRows(at: [indexPath], with: .none)
}
//then on the cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckList") as! TableViewCell
cell.lbTitle.text = "\(self.deviceData[indexPath.row].brandName) \(self.deviceData[indexPath.row].modelName)"
cell.lbDetail.text = "\(self.deviceData[indexPath.row].operatorName) \(self.deviceData[indexPath.row].version), \(self.deviceData[indexPath.row].browserName) \(self.deviceData[indexPath.row].version)"
//cell.selectionStyle = .none
cell.setSelected(data[indexPath.row].selected, animated: true)
return cell
}
that way the tableView and your data are synchronized.
Upvotes: 0
Reputation: 285039
In Swift the most efficient way is a callback closure.
It's pretty easy: No protocols, no target/action, no delegate, no tags, no index paths, no view math, no @objc
attribute.
In the model representing deviceData
add a property isSelected
var isSelected = false
In the cell add a property callback
and an IBAction
. Connect the button to the IBAction
. In the IBAction
the selection state is toggled and the callback is called
var callback : ((UIButton) -> Void))?
@IBAction func buttonPressed(_ sender : UIButton) {
sender.isSelected.toggle()
callback?(sender)
}
In the view controller set and handle the callback in cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let rowpath = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "CheckList") as! TableViewCell
let data = self.deviceData[indexPath.row]
cell.lbTitle.text = "\(data.brandName) \(data.modelName)"
cell.lbDetail.text = "\(data.operatorName) \(data.version), \(data.browserName) \(data.version)"
cell.btnCheckMark.isSelected = data.isSelected
cell.callback { button in
self.deviceData[indexPath.row].isSelected = button.isSelected
// or if deviceData is a class with reference semantics
// data.isSelected = button.isSelected
}
return cell
}
Upvotes: 3
Reputation: 380
Use tableView.indexPath(for: UITableViewCell)
to get the whole IndexPath for your cell.
Example use:
@objc func checkMarkButton(sender: UIButton) {
// do something else
guard let cell = sender.superview as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else {
return
}
print(indexPath.row)
}
Use if let
instead of guard let
, if you do not want to terminate execution.
Upvotes: 0
Reputation: 145
You can subclass button with the property of Indexpath and use that button instead.
So:
class ButtonWithIndexPath : UIButton {
var indexPath:IndexPath?
}
Then assign the property to your new button in the cellforRowAt method in your table when you assign all the other properties.
Upvotes: 0