Swift tableview cell select to change checkbox image - ios

I am trying to implement custom button check box in tableview cell. I have done checkbox when user clicks cell button it can change check and uncheck but if you click tableview cell also I needs to operate the check box
If possible please give some idea for radio button functionality because I am doing both.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.animals[indexPath.row]
if selectedRows.contains(indexPath)
{
cell.checkBox.setImage(UIImage(named:"check.png"), for: .normal)
}
else
{
cell.checkBox.setImage(UIImage(named:"uncheck.png"), for: .normal)
}
cell.checkBox.tag = indexPath.row
cell.checkBox.addTarget(self, action: #selector(checkBoxSelection(_:)), for: .touchUpInside)
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
}
#objc func checkBoxSelection(_ sender:UIButton)
{
let selectedIndexPath = IndexPath(row: sender.tag, section: 0)
if self.selectedRows.contains(selectedIndexPath)
{
self.selectedRows.remove(at: self.selectedRows.index(of: selectedIndexPath)!)
}
else
{
self.selectedRows.append(selectedIndexPath)
}
self.tableView.reloadData()
}

You can get the selected cell in didSelectRowAt delegate and set the checkmark.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? MyCustomCell else {
return
}
if self.selectedRows.contains(indexPath) {
self.selectedRows.remove(at: self.selectedRows.index(of: indexPath)!)
cell.checkBox.setImage(UIImage(named:"unccheck.png"), for: .normal)
} else {
self.selectedRows.append(indexPath)
cell.checkBox.setImage(UIImage(named:"check.png"), for: .normal)
}
}

// https://stackoverflow.com/questions/47300399/how-to-select-table-view-row-selection-with-custom-checkbox-button
import UIKit
class MyCustomCell: UITableViewCell {
#IBOutlet weak var checkBox: UIButton!
#IBOutlet weak var myCellLabel: UILabel!
}
class UpdateViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
// These strings will be the data for the table view cells
var animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
var selectedRows:[IndexPath] = []
// These are the colors of the square views in our table view cells.
// In a real project you might use UIImages.
let colors = [UIColor.blue, UIColor.yellow, UIColor.magenta, UIColor.red, UIColor.brown]
// Don't forget to enter this in IB also
let cellReuseIdentifier = "cell"
override func viewDidLoad() {
super.viewDidLoad()
// Remove unused array
tableView.tableFooterView = UIView()
//tableView.allowsSelection = false
tableView.delegate = self
tableView.dataSource = self
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.animals.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.animals[indexPath.row]
if selectedRows.contains(indexPath)
{
cell.checkBox.setImage(UIImage(named:"check.png"), for: .normal)
}
else
{
cell.checkBox.setImage(UIImage(named:"uncheck.png"), for: .normal)
}
cell.checkBox.tag = indexPath.row
cell.checkBox.addTarget(self, action: #selector(checkBoxSelection(_:)), for: .touchUpInside)
return cell
}
// method to run when table view cell is tapped
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped cell number \(indexPath.row).")
guard let cell = tableView.cellForRow(at: indexPath) as? MyCustomCell else {
return
}
if self.selectedRows.contains(indexPath) {
self.selectedRows.remove(at: self.selectedRows.index(of: indexPath)!)
cell.checkBox.setImage(UIImage(named:"uncheck.png"), for: .normal)
} else {
self.selectedRows.append(indexPath)
cell.checkBox.setImage(UIImage(named:"check.png"), for: .normal)
let indexPath = tableView.indexPathForSelectedRow //optional, to get from any UIButton for example
let currentCell = tableView.cellForRow(at: indexPath!) as! MyCustomCell
print(currentCell.myCellLabel.text ?? "")
}
}
#objc func checkBoxSelection(_ sender:UIButton)
{
let selectedIndexPath = IndexPath(row: sender.tag, section: 0)
if self.selectedRows.contains(selectedIndexPath)
{
self.selectedRows.remove(at: self.selectedRows.index(of: selectedIndexPath)!)
}
else
{
self.selectedRows.append(selectedIndexPath)
let center = sender.center
let point = sender.superview!.convert(center, to:self.tableView)
let indexPath = self.tableView.indexPathForRow(at: point)
let cell = self.tableView.cellForRow(at: indexPath!) as! MyCustomCell //Add superview on the basis of your button hierarchy in the cell
let cell_labelvalue = cell.myCellLabel!.text
print(cell_labelvalue ?? "")
}
self.tableView.reloadData()
}
#IBAction func selectAllBtnAction(_ sender: UIBarButtonItem) {
self.selectedRows = getAllIndexPaths()
self.tableView.reloadData()
}
func getAllIndexPaths() -> [IndexPath] {
var indexPaths: [IndexPath] = []
for j in 0..<tableView.numberOfRows(inSection: 0) {
indexPaths.append(IndexPath(row: j, section: 0))
}
return indexPaths
}
#IBAction func cancelPopup(_ sender: Any) {
self.removeAnimate()
}
#IBAction func donePopUp(_ sender: AnyObject) {
self.removeAnimate()
}
func showAnimate()
{
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0;
UIView.animate(withDuration: 0.25, animations: {
self.view.alpha = 1.0
self.view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
});
}
func removeAnimate()
{
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished)
{
self.view.removeFromSuperview()
}
});
}
}

Related

UITableView reloadRows() called by action button inside custom UITableViewCell always lags one step behind

I have a UITableView and I made a custom cellview to add a button inside each cell, the button is supposed to change its color when clicked.
Although the data is updated and printed correctly, the view always lags one step behind i.e. when I click the first button it doesn't change its color until I click the next one.I suspect that the reloadRows() function causes this problem when called from inside the tableview Cell.
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var mlist = [["1","2","3"], ["4","5","6","7","8"],["9","10"],["11","12"],["13","14"]]
var leagues = ["LaLiga", "Premier League", "Bundesliga", "Serie A", "Ligue 1"]
var hidden = Set<Int>()
#IBOutlet weak var tbl: UITableView!
var fv = Set<IndexPath>()
func indxs(_ section:Int) -> [IndexPath] {
var indxs = [IndexPath]()
for row in 0..<mlist[section].count {
indxs.append(IndexPath(row: row, section: section))
}
return indxs
}
#objc
private func hideSection(sender: UIButton) {
let section = sender.tag
if hidden.contains(section) {
hidden.remove(section)
tbl.insertRows(at: indxs(section), with: .fade)
}else{
hidden.insert(section)
tbl.deleteRows(at: indxs(section), with: .fade)
}
}
func cellMethod(cell: UITableViewCell) {
guard let i = tbl.indexPath(for: cell) else { return }
if fv.contains(i){fv.remove(i)}else{fv.insert(i)}
tbl.reloadRows(at: [i], with: .none)
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if hidden.contains(section) {
return 0
}
return mlist[section].count
}
func numberOfSections(in tableView: UITableView) -> Int {
return mlist.count
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let sectionButton = UIButton()
sectionButton.setTitle(leagues[section], for: .normal)
sectionButton.backgroundColor = .purple
sectionButton.tag = section
sectionButton.addTarget(self, action: #selector(self.hideSection(sender:)), for: .touchUpInside)
return sectionButton
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! mcell
cell.link = self
cell.textLabel?.text = mlist[indexPath.section][indexPath.row]
if fv.contains(indexPath){
cell.accessoryView?.tintColor = .orange
}else{
cell.accessoryView?.tintColor = .gray
}
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
tbl.register(mcell.self, forCellReuseIdentifier: "cell1")
}
}
import UIKit
class mcell: UITableViewCell{
var link:ViewController?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let starButton = UIButton(type: .system)
starButton.setImage(#imageLiteral(resourceName: "fav_star"), for: .normal)
starButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
starButton.tintColor = .red
starButton.addTarget(self, action: #selector(handleMarkAsFavorite), for: .touchUpInside)
accessoryView = starButton
}
#objc private func handleMarkAsFavorite() {
print(self.textLabel!.text!)
link?.cellMethod(cell: self)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Giving your cell a reference to its controller is a bad pattern.
You're much better off using a closure to let the cell "call back" to the controller when you tap the star.
Here's an update to your cell class:
class mcell: UITableViewCell{
// "callback" closure to tell the controller that the Star was tapped
var starWasTapped: (() -> ())?
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
let starButton = UIButton(type: .system)
starButton.setImage(#imageLiteral(resourceName: "fav_star"), for: .normal)
starButton.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
starButton.tintColor = .red
starButton.addTarget(self, action: #selector(handleMarkAsFavorite), for: .touchUpInside)
accessoryView = starButton
}
#objc private func handleMarkAsFavorite() {
print(self.textLabel!.text!)
// tell the controller the Star was tapped
starWasTapped?()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Then, your cellForRowAt will look like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! mcell
cell.textLabel?.text = mlist[indexPath.section][indexPath.row]
if fv.contains(indexPath){
cell.accessoryView?.tintColor = .orange
}else{
cell.accessoryView?.tintColor = .gray
}
// set the cell's "callback" closure
cell.starWasTapped = { [weak self] in
guard let self = self else { return }
if self.fv.contains(indexPath){self.fv.remove(indexPath)}else{self.fv.insert(indexPath)}
self.tbl.reloadRows(at: [indexPath], with: .none)
}
return cell
}
and now you have no need for the separate func cellMethod(...)
I found a solution here and it worked for me https://stackoverflow.com/a/39416618/14061160 , by adding action to the button to force triggering the delegate function (didSelectRowAtIndexPath) and inside the delegate function I apply reloadRows()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! mcell
// cell.link = self
cell.textLabel?.text = mlist[indexPath.section][indexPath.row]
if fv.contains(indexPath){
cell.accessoryView?.tintColor = .orange
}else{
cell.accessoryView?.tintColor = .gray
}
cell.starWasTapped = { [weak self] in
guard let self = self else { return }
if self.fv.contains(indexPath){self.fv.remove(indexPath)}else{self.fv.insert(indexPath)}
self.tbl.delegate!.tableView?(self.tbl, didSelectRowAt: indexPath)
}
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tbl.reloadRows(at: [indexPath], with: .fade)
}

Labels in UITableViewCell Shifts/ Disappears while expanding the cell

I am trying to create expand/ collapse tableView having multiple labels, textViews and images. The problem is when I expand a cell, the top most label (Black Text/ Blue background in image) disappears and then comes back when cell updates. Is there any proper solution to fix this type of problem? Is this related to reloadRows?
// ViewController Class:
private func bindTableView() {
guard let tableView = self.planServicesTableView,
let viewModel = self.viewModel else {
return
}
tableView.estimatedRowHeight = 130
tableView.rowHeight = UITableView.automaticDimension
let dataSource = RxTableViewSectionedReloadDataSource<PlanServiceSection>(configureCell:
{(dataSource: TableViewSectionedDataSource<PlanServiceSection>,
tableView: UITableView,
indexPath: IndexPath,
item: PlanServiceSection.Item) -> UITableViewCell in
let cell = tableView.dequeueReusableCell(withIdentifier: item.cellType.cellIdent, for: indexPath)
if let planServiceCell = cell as? PlanServiceDescriptionTableViewCell {
planServiceCell.setCollapsed(collapsed:(viewModel.cellIsExpanded(at: indexPath)) ? false : true)
planServiceCell.configureCell(item: item)
planServiceCell.upgradeTextView.sizeToFit()
planServiceCell.featureDisclaimerTextView.sizeToFit()
}
if let disclaimerCell = cell as? PlanDisclaimerTableViewCell {
disclaimerCell.setCollapsed(collapsed: (viewModel.cellIsExpanded(at: indexPath)) ? false : true)
disclaimerCell.configureCell(item: item)
disclaimerCell.disclaimerDescriptionTextView.sizeToFit()
}
return cell
})
viewModel.dataSource = dataSource
tableView.tableFooterView = UIView()
tableView.delegate = self
viewModel.sections.bind(to: tableView.rx.items(dataSource: dataSource))
.disposed(by: self.disposeBag)
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let descriptionCell = tableView.cellForRow(at: indexPath) as? PlanServiceDescriptionTableViewCell {
descriptionCell.setCollapsed(collapsed: shouldCollapseCell(indexPath: indexPath))
}
if let disclaimerCell = tableView.cellForRow(at: indexPath) as? PlanDisclaimerTableViewCell {
disclaimerCell.setCollapsed(collapsed: shouldCollapseCell(indexPath: indexPath))
}
DispatchQueue.main.async {
tableView.reloadRows(at: [indexPath], with: .automatic)
}
}
private func shouldCollapseCell(indexPath: IndexPath) -> Bool {
if let isExpanded = viewModel?.cellIsExpanded(at: indexPath),
isExpanded {
self.viewModel?.removeExpandedIndexPath(indexPath)
return true
}
self.viewModel?.addExpandedIndexPath(indexPath)
return false
}
// TableViewCell Class:
func setCollapsed(collapsed: Bool) {
self.toggleArrowImage.image = (collapsed ? expandImage : collapseImage)
self.stackView.isHidden = collapsed
}
you need to do the following to fix it
var cellHeights:[IndexPath:CGFloat] = [ : ]
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
if let height = cellHeights[indexPath]{
return height
}
return UITableView.automaticDimension
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
}
let me know once you have tested!

how to use indexpath.row outside the table function

my tableview is
struct country : Decodable {
let name : String
let capital : String
let region : String
}
class ViewController: UIViewController {
var countries = [country]()
let color = UIColor()
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
let jsonurl = "https://restcountries.eu/rest/v2/all"
let url = URL(string: jsonurl)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.countries = try JSONDecoder().decode([country].self, from: data!)
} catch {
print("Error")
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}.resume()
// 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 redButtonAction(_ sender: UIButton) {
let index = IndexPath.init(row: 0, section: 0)
let cell = tableview.cellForRow(at: index)
}
}
extension ViewController:UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return countries.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath:
IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = countries[indexPath.row].name.capitalized
let cellnumber = indexPath.row
return cell
}
now I want to use cellnumber or indexpath.row in my button action. I do this but i can not get indexpath.row
I want to do when I pressed the button at that time the odd number cell of the tableview 's background color change in red and even number cell 's background color change blue. But problem is out side tableview function I get only one value not whole number of array. In above program if I print cellnumber we get whole number of cell.
table view cell is reusable and you can get only visible cell's indexPath so you should reload table view on clicked of button and need to put condition if you want to change color or not in cellForRowAt
Code for change row color on button clicked
struct country : Decodable {
let name : String
let capital : String
let region : String
}
class ViewController : UIViewController {
var countries = [country]()
let color = UIColor()
var needToChangeColor : Bool = false
#IBOutlet weak var tableview: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableview.delegate = self
tableview.dataSource = self
let jsonurl = "https://restcountries.eu/rest/v2/all"
let url = URL(string: jsonurl)
URLSession.shared.dataTask(with: url!) { (data, response, error) in
do {
self.countries = try JSONDecoder().decode([country].self, from: data!)
} catch {
print("Error")
}
DispatchQueue.main.async {
self.tableview.reloadData()
}
}.resume()
// 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 redButtonAction(_ sender: UIButton) {
self.needToChangeColor = true
self.tableview.reloadData()
}
}
extension ViewController :UITableViewDelegate,UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int {
return countries.count
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath:
IndexPath) -> CGFloat {
return 40
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = countries[indexPath.row].name.capitalized
if needToChangeColor == true {
if indexPath.row % 2 == 0 {
cell.backgroundColor = UIColor.blue
} else {
cell.backgroundColor = UIColor.red
}
} else {
cell.backgroundColor = UIColor.white
}
return cell
}
}
The very basic solution would be adding a tag for your button in cellForRowAt. It will work in case you have only one section.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = countries[indexPath.row].name.capitalized
cell.button.tag = indexPath.row
return cell
}
Now in your method
#IBAction func redButtonAction(_ sender: UIButton) {
let index = IndexPath.init(row: sender.tag, section: 0)
let cell = tableview.cellForRow(at: index)
}
Unfortunately this approach will not be so efficient if you add any complexity to your tableView layout.

how to select Table View row selection with custom checkbox button?

How to select tableview row with custom button . i have another button called select all its outside of the table view my question is while clicking outside of the tableview button how to select and deselect inside tableview rows? At the same time i could able to select single row in the tableview ? how to do it in swift 3? This is my code in cellforrow method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "Custom"
var cell: TStudentAttendanceCell! = tableView.dequeueReusableCell(withIdentifier: identifier) as? TStudentAttendanceCell
if cell == nil {
tableView.register(UINib(nibName: "TStudentAttendanceCell", bundle: nil), forCellReuseIdentifier: identifier)
cell = tableView.dequeueReusableCell(withIdentifier: identifier) as? TStudentAttendanceCell
}
print("studentAttendanvceArray--",studentAttendanceArray.object(at: indexPath.row) )
var localDic :NSDictionary!
localDic = studentAttendanceArray.object(at: indexPath.row) as! NSDictionary
Common.sharedInstance.StopActivity()
cell.profile_img.image = self.image
cell.name_lbl.text = localDic["student_name"] as? String
cell.selectionStyle = UITableViewCellSelectionStyle.none
cell.contentView.backgroundColor = UIColor.clear
let whiteRoundedView : UIView = UIView(frame: CGRect(x: 10, y: 8, width: self.view.frame.size.width - 20, height: 90))
whiteRoundedView.layer.backgroundColor = CGColor(colorSpace: CGColorSpaceCreateDeviceRGB(), components: [1.0, 1.0, 1.0, 0.9])
whiteRoundedView.layer.masksToBounds = false
whiteRoundedView.layer.cornerRadius = 2.0
whiteRoundedView.layer.shadowOffset = CGSize(width: -1, height: 1)
whiteRoundedView.layer.shadowOpacity = 0.2
cell.contentView.addSubview(whiteRoundedView)
cell.contentView.sendSubview(toBack: whiteRoundedView)
return cell
}
ViewController
class ViewController: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet var tableView: UITableView!
var allStudentsArr:[[String:String]] = []
var selectedRows:[IndexPath] = []
override func viewDidLoad() {
super.viewDidLoad()
tableView.allowsSelection = false
allStudentsArr = [["name":"name1"],["name":"name2"],["name":"name3"],["name":"name4"],["name":"name5"],["name":"name6"],["name":"name7"],["name":"name8"]]
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return allStudentsArr.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! CustomTableViewCell
cell.nameLbl.text = allStudentsArr[indexPath.row]["name"]
if selectedRows.contains(indexPath)
{
cell.checkBox.setImage(UIImage(named:"selected"), for: .normal)
}
else
{
cell.checkBox.setImage(UIImage(named:"unselected"), for: .normal)
}
cell.checkBox.tag = indexPath.row
cell.checkBox.addTarget(self, action: #selector(checkBoxSelection(_:)), for: .touchUpInside)
return cell
}
#objc func checkBoxSelection(_ sender:UIButton)
{
let selectedIndexPath = IndexPath(row: sender.tag, section: 0)
if self.selectedRows.contains(selectedIndexPath)
{
self.selectedRows.remove(at: self.selectedRows.index(of: selectedIndexPath)!)
}
else
{
self.selectedRows.append(selectedIndexPath)
}
self.tableView.reloadData()
}
#IBAction func selectAllBtnAction(_ sender: UIBarButtonItem) {
self.selectedRows = getAllIndexPaths()
self.tableView.reloadData()
}
func getAllIndexPaths() -> [IndexPath] {
var indexPaths: [IndexPath] = []
for j in 0..<tableView.numberOfRows(inSection: 0) {
indexPaths.append(IndexPath(row: j, section: 0))
}
return indexPaths
}
}
Custom Cell
class CustomTableViewCell: UITableViewCell {
#IBOutlet var nameLbl: UILabel!
#IBOutlet var checkBox: UIButton!
}
Thats how you can programatically select all rows of a single section
#IBAction func didTapSelectAllButton(sender: UIButton) {
let totalRows = tableView.numberOfRows(inSection: 0)// Make some logic if you have more than 1 section
for row in 0..<totalRows {
let indexPath = IndexPath(row: row, section: 0)
tableView.selectRow(at: indexPath, animated: false, scrollPosition: .none)
}
}
If you don't want to use default check box of tableView then disable multipleSelection from tableView and implement logic using an extra global array.
var selectedArrayIndex = [Int]()
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if selectedArrayIndex.contains(indexPath.row) {
selectedArrayIndex.remove(at: selectedArrayIndex.index(of: indexPath.row)!)
}else {
selectedArrayIndex.append(indexPath.row)
}
}
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if selectedArrayIndex.contains(indexPath.row) {
// Enable You Check
cell.checkBoxView.isHidden = false
}else {
cell.checkBoxView.isHidden = true
}
}
#IBAction func didTapSelectAllButton(sender: UIButton) {
let totalRows = tableView.numberOfRows(inSection: 0)// Make some logic if you have more than 1 section
selectedArrayIndex.removeAll()
for row in 0..<totalRows {
selectedArrayIndex.append(row)
}
}

Swift 3.0 How to Change a Button Title Using TableView Cells

I have a table view with subclassed table view cells. I have attached a button and hooked this up to the table view cell's VC. I want the button to say 'Add' upon loading, 'Subtract' when clicked and back to 'Add' when clicked again. But I am having trouble understanding how I can relate the row number with the state of the button.
VC with table view:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TVCell
cell.cellDelegate = self
if (searchActive) {
cell.textLabel?.text = filtered[indexPath.row]
} else {
cell.textLabel?.text = data[indexPath.row]
}
return cell
}
func didPressButton(cell: TVCell) {
guard let indexPath = self.tableView.indexPath(for: cell) else {
return
}
print("Button tapped on row \(indexPath.row)")
}
VC for table view cell:
protocol TVCellDelegate : class {
func didPressButton(cell: TVCell)
}
class TVCell: UITableViewCell {
#IBOutlet weak var addButton: UIButton!
weak var cellDelegate: TVCellDelegate?
override func prepareForReuse() {
super.prepareForReuse()
self.cellDelegate = nil
}
// connect the button from your cell with this method
#IBAction func buttonPressed(sender: UIButton) {
self.cellDelegate?.didPressButton(cell: self)
}
}
Store the buttons' state in your view controller and change the text after reusing and button pressing.
VC with TableView:
var isSubtracting = [IndexPath: Bool]()
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TVCell
cell.cellDelegate = self
if isSubtracting[indexPath] ?? false {
cell.addButton.setTitle("Add", for: .normal)
} else {
cell.addButton.setTitle("Subtract", for: .normal)
}
cell.indexPath = indexPath
if(searchActive) {
cell.textLabel?.text = filtered[indexPath.row]
} else {
cell.textLabel?.text = data[indexPath.row]
}
return cell
}
func didPressButton(indexPath: IndexPath) {
guard let cell = tableView.cellForRow(at: indexPath) as? TVCell else {
return
}
if isSubtracting[indexPath] ?? false {
isSubtracting[indexPath] = false
cell.addButton.setTitle("Subtract", for: .normal)
} else {
isSubtracting[indexPath] = true
cell.addButton.setTitle("Add", for: .normal)
}
}
VC for table view cell:
protocol TVCellDelegate : class {
func didPressButton(indexPath: IndexPath)
}
class TVCell: UITableViewCell {
#IBOutlet weak var addButton: UIButton!
weak var cellDelegate: TVCellDelegate?
var indexPath: IndexPath!
override func prepareForReuse() {
super.prepareForReuse()
self.cellDelegate = nil
}
// connect the button from your cell with this method
#IBAction func buttonPressed(sender: UIButton) {
self.cellDelegate?.didPressButton(indexPath: indexPath)
}
}
You can add the title's for the states of buttons like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! TVCell
cell.cellDelegate = self
cell.indexPath = indexPath
cell.addButton.setTitle("Add",for: .normal)
cell.addButton.setTitle("Subtract",for: .selected)
if(searchActive) {
cell.textLabel?.text = filtered[indexPath.row]
} else {
cell.textLabel?.text = data[indexPath.row]
}
return cell
}
and tableViewCell
protocol TVCellDelegate : class {
func didPressButton(indexPath: IndexPath)
}
class TVCell: UITableViewCell {
#IBOutlet weak var addButton: UIButton!
weak var cellDelegate: TVCellDelegate?
var indexPath: IndexPath!
override func prepareForReuse() {
super.prepareForReuse()
self.cellDelegate = nil
}
// connect the button from your cell with this method
#IBAction func buttonPressed(sender: UIButton) {
if(sender.isSelected){
sender.isSelected = false
}else{
sender.isSelected = true
}
self.cellDelegate?.didPressButton(indexPath: indexPath)
}
}

Resources