How to Select Table View Row Selection with Custom Checkbox Button

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

Sample Image

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

Swift tableview cell select to change checkbox image

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

uitableview:In a same Row selected button should checked and remaining buttons should be unchecked automaticallyy

I think that you should use model to set for UITableViewCell. Your model's .h file like :

#import <Foundation/Foundation.h>

typedef enum : NSInteger {
UNKNOWN = 0,
VERY_GOOD = 1,
GOOD = 2,
AVERAGE = 3,
BELOW_AVERAGE = 4
}RangeMark;

@interface CellModel : NSObject

@property(nonatomic, assign) RangeMark range;

@end

.m file like:

#import "CellModel.h"

@implementation CellModel

@end

than you should init a table cell with .xib file looks like:
Sample Image

and its .h file like :

#import <UIKit/UIKit.h>
#import "CellModel.h"

@interface TableViewCell : UITableViewCell

@property (weak, nonatomic) IBOutlet UIButton *veryGoodButton;
@property (weak, nonatomic) IBOutlet UIButton *goodButton;
@property (weak, nonatomic) IBOutlet UIButton *averageButton;
@property (weak, nonatomic) IBOutlet UIButton *belowAverageButton;

- (void)setupCellWithModel:(CellModel*)model;

@end

its .m file like :

#import "TableViewCell.h"

@implementation TableViewCell

- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];

// Configure the view for the selected state
}

- (void)setupCellWithModel:(CellModel *)model {

if(model.range == VERY_GOOD) {
self.veryGoodButton.backgroundColor = [UIColor greenColor];
}
else if(model.range == GOOD) {
self.goodButton.backgroundColor = [UIColor blueColor];
}
else if(model.range == AVERAGE) {
self.averageButton.backgroundColor = [UIColor yellowColor];
}
else if(model.range == BELOW_AVERAGE) {
self.belowAverageButton.backgroundColor = [UIColor redColor];
}
}

- (void)prepareForReuse {
[super prepareForReuse];
self.veryGoodButton.backgroundColor = [UIColor lightGrayColor];
self.goodButton.backgroundColor = [UIColor lightGrayColor];
self.averageButton.backgroundColor = [UIColor lightGrayColor];
self.belowAverageButton.backgroundColor = [UIColor lightGrayColor];
}

@end

Finally your view controller .h file should look like :

#import <UIKit/UIKit.h>
#import "TableViewCell.h"

@interface ViewController : UIViewController

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@end

and .m file should look like :

#import "ViewController.h"

@interface ViewController () <UITableViewDelegate, UITableViewDataSource>{
NSMutableArray<CellModel*> *modelList;
}

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.tableView.estimatedRowHeight = 600;
self.tableView.rowHeight = UITableViewAutomaticDimension;

modelList = [NSMutableArray<CellModel*> new];

for (int i=0; i<50; i++) {
CellModel *cellModel = [[CellModel alloc] init];
cellModel.range = UNKNOWN;
[modelList addObject:cellModel];
}
}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

- (nonnull UITableViewCell *)tableView:(nonnull UITableView *)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {

TableViewCell *cell = (TableViewCell*)[tableView dequeueReusableCellWithIdentifier:@"TableViewCell"];
if (cell == nil)
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"TableViewCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}

[cell setupCellWithModel:[modelList objectAtIndex:indexPath.row]];

cell.veryGoodButton.tag = indexPath.row;
cell.goodButton.tag = indexPath.row;
cell.averageButton.tag = indexPath.row;
cell.belowAverageButton.tag = indexPath.row;

[cell.veryGoodButton addTarget:self action:@selector(veryGood:) forControlEvents:UIControlEventTouchUpInside];

[cell.goodButton addTarget:self action:@selector(good:) forControlEvents:UIControlEventTouchUpInside];

[cell.averageButton addTarget:self action:@selector(average:) forControlEvents:UIControlEventTouchUpInside];

[cell.belowAverageButton addTarget:self action:@selector(belowAverage:) forControlEvents:UIControlEventTouchUpInside];

return cell;

return nil;
}

- (NSInteger)tableView:(nonnull UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return modelList.count;
}

- (void) veryGood:(UIButton*)sender {
[modelList objectAtIndex: sender.tag].range = VERY_GOOD;
[self setCellDynamicly:[NSIndexPath indexPathForRow:sender.tag inSection:0] withCellModel:[modelList objectAtIndex: sender.tag]];
}

- (void) good:(UIButton*)sender {
[modelList objectAtIndex: sender.tag].range = GOOD;
[self setCellDynamicly:[NSIndexPath indexPathForRow:sender.tag inSection:0] withCellModel:[modelList objectAtIndex: sender.tag]];
}

- (void) average:(UIButton*)sender {
[modelList objectAtIndex: sender.tag].range = AVERAGE;
[self setCellDynamicly:[NSIndexPath indexPathForRow:sender.tag inSection:0] withCellModel:[modelList objectAtIndex: sender.tag]];
}

- (void) belowAverage:(UIButton*)sender {
[modelList objectAtIndex: sender.tag].range = BELOW_AVERAGE;
[self setCellDynamicly:[NSIndexPath indexPathForRow:sender.tag inSection:0] withCellModel:[modelList objectAtIndex: sender.tag]];
}

- (void)setCellDynamicly:(NSIndexPath*)indexPath withCellModel:(CellModel*)cellModel {
TableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
[cell prepareForReuse];
[cell setupCellWithModel:cellModel];
}

@end

that s all :)

At the end app looks like :

Sample Image

Why my checkbox in custom cell shows different behaviour while selecting and scrolling in swift?

This is happening because you are reusing the TableViewCell, To solve your problem you can try something like this, first create an array of Int that give you selected row and use that array inside cellForRowAtIndexPath method.

var selectedItems = [Int]()

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomOneCell = AddO nTableView.dequeueReusableCellWithIdentifier("addoncell") as! CustomOneCell
let item: AddOnItems = ADDONITEMS[indexPath.row]
cell.addOnName.text = item.name
cell.addOnPrice.text = "£\(item.price!)"
cell.checkBoxBtn.tag = indexPath.row
if (selectedItems.contains(indexPath.row)) {
cell.checkBoxBtn.setImage(UIImage(named:"checkbox untick.png"), forState: .Normal)
}
else {
cell.checkBoxBtn.setImage(UIImage(named: "checkboxredtick.png"), forState: .Normal)
}
cell.checkBoxBtn.addTarget(self, action:#selector(self.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
return cell
}

func buttonClicked(sender: UIButton) {
if (self.selectedItems.contains(sender.tag)) {
let index = self.selectedItems.indexOf(sender.tag)
self.selectedItems.removeAtIndex(index)
}
else {
self.selectedItems.append(sender.tag)
}
self.tableView.reloadData()
}

check / uncheck the check box by tapping the cell in table view and how to know which cell has checked or unchecked

In order to solve your issue, as @El Capitan mentioned, you will need to use the didSelectRowAtIndexPath method to change its states. Your codes should look something along the lines of this:

// Declare a variable which stores checked rows. UITableViewCell gets dequeued and restored as you scroll up and down, so it is best to store a reference of rows which has been checked
var rowsWhichAreChecked = [NSIndexPath]()

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell:FavCell = tableView.cellForRowAtIndexPath(indexPath) as! FavCell
// cross checking for checked rows
if(rowsWhichAreChecked.contains(indexPath) == false){
cell.checkBox.isChecked = true
rowsWhichAreChecked.append(indexPath)
}else{
cell.checkBox.isChecked = false
// remove the indexPath from rowsWhichAreCheckedArray
if let checkedItemIndex = rowsWhichAreChecked.indexOf(indexPath){
rowsWhichAreChecked.removeAtIndex(checkedItemIndex)
}
}
}

To redisplay cells which have been checked before after scrolling the rows out of view, at your cellForRowAtIndexPath, perform the same checking against rowsWhichAreChecked array and set its states accordingly.

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

let cell:FavCell = self.FavTableView.dequeueReusableCellWithIdentifier("FCell") as! FavCell
cell.FLabel1.text=arrDict[indexPath.section] .valueForKey("favourite_name") as? String
cell.FLabel2.text=arrDict[indexPath.section] .valueForKey("favourite_address") as? String

if(rowsWhichAreChecked.contains(indexPath) == false){
cell.checkBox.isChecked = true
}else{
cell.checkBox.isChecked = false
}
}
return cell
}

EDITED ANSWER

I have got your code to work but I had to make some modifications to your Checkbox class and ViewController

Checkbox.swift

class CheckBoxButton: UIButton {

// Images
let checkedImage = UIImage(named: "CheckBoxChecked")! as UIImage
let uncheckedImage = UIImage(named: "CheckBoxUnChecked")! as UIImage

// Bool property
var isChecked: Bool = false {
didSet{
if isChecked == true {
self.setImage(uncheckedImage, forState: .Normal)
} else {
self.setImage(checkedImage, forState: .Normal)
}
}
}

override func awakeFromNib() {
self.userInteractionEnabled = false
// self.addTarget(self, action: #selector(CheckBoxButton.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
// self.isChecked = false
}

func buttonClicked(sender: UIButton) {
if sender == self {
if isChecked == true {
isChecked = false
} else {
isChecked = true
}
}
}

}

ViewController.swift

class FavVC: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet weak var FavTableView: UITableView!

var rowsWhichAreChecked = [NSIndexPath]()

//var FData = [FavouritesData]()

var arrDict :NSMutableArray=[]

let cellSpacingHeight: CGFloat = 5 // cell spacing from each cell in table view

override func viewDidLoad() {

self.FavTableView.delegate = self
self.FavTableView.dataSource = self

super.viewDidLoad()

self.jsonParsingFromURL()

let nib = UINib(nibName:"FavCell", bundle: nil)

FavTableView.registerNib(nib, forCellReuseIdentifier: "FCell")

}

// web services method
func jsonParsingFromURL ()
{
// let token = NSUserDefaults.standardUserDefaults().valueForKey("access_token") as? String

let url = NSURL(string: "some url")

let session = NSURLSession.sharedSession()

let request = NSURLRequest(URL: url!)

let dataTask = session.dataTaskWithRequest(request) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
// print("done, error: \(error)")

if error == nil
{

dispatch_async(dispatch_get_main_queue())
{
self.arrDict=(try! NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSMutableArray
if (self.arrDict.count>0)
{
self.FavTableView.reloadData()
}
}

}

}
dataTask.resume()

//
// let StringUrl = "http"+token!

// let url:NSURL = NSURL(string: StringUrl)!

// if let JSONData = NSData(contentsOfURL: url)
// {
// if let json = (try? NSJSONSerialization.JSONObjectWithData(JSONData, options: [])) as? NSDictionary
// {
// for values in json
// {
// self.FData.append()
// }

// if let reposArray = json["data"] as? [NSDictionary]
// {
//
// for item in reposArray
// {
// let itemObj = item as? Dictionary<String,AnyObject>
//
// let b_type = itemObj!["business_type"]?.valueForKey("type")
//
// //self.Resultcount.text = "\(b_type?.count) Results"
//
// if (b_type as? String == "Taxis")
// {
//
// self.FData.append(FavouritesData(json:item))
//
// }
// }
// }

// }
// }

}

func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
// return self.FData.count
return self.arrDict.count
}

// number of rows
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 1
}

// height for each cell
func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
{
return cellSpacingHeight
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{

let cell:FavCell = self.FavTableView.dequeueReusableCellWithIdentifier("FCell") as! FavCell
cell.FLabel1.text=arrDict[indexPath.section] .valueForKey("favourite_name") as? String
cell.FLabel2.text=arrDict[indexPath.section] .valueForKey("favourite_address") as? String

let isRowChecked = rowsWhichAreChecked.contains(indexPath)

if(isRowChecked == true)
{
cell.checkbox.isChecked = true
cell.checkbox.buttonClicked(cell.checkbox)
}else{
cell.checkbox.isChecked = false
cell.checkbox.buttonClicked(cell.checkbox)
}

return cell
}

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell:FavCell = tableView.cellForRowAtIndexPath(indexPath) as! FavCell
// cross checking for checked rows
if(rowsWhichAreChecked.contains(indexPath) == false){
cell.checkbox.isChecked = true
cell.checkbox.buttonClicked(cell.checkbox)
rowsWhichAreChecked.append(indexPath)
}else{
cell.checkbox.isChecked = false
cell.checkbox.buttonClicked(cell.checkbox)
// remove the indexPath from rowsWhichAreCheckedArray
if let checkedItemIndex = rowsWhichAreChecked.indexOf(indexPath){
rowsWhichAreChecked.removeAtIndex(checkedItemIndex)
}
}
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

}


Related Topics



Leave a reply



Submit