TableView with multiple section and with radio button and checkbox button - swift

This I what I trying to achieve:
There is no problem in populating all the datas, but the problem is how to let user to choose only one row for radio section and allow more than one row for checkbox section, I am stuck in this part. Here is the code for now:
#objc func checkboxSelected(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if(sender.isSelected == true)
{
sender.setImage(UIImage(named: "red_checkbox"), for: UIControlState.normal)
}
else
{
sender.setImage(UIImage(named: "checkbox2"), for: UIControlState.normal)
}
}
#objc func radioBtnSelected(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if(sender.isSelected == true)
{
sender.setImage(UIImage(named: "radio_red"), for: UIControlState.normal)
}
else
{
sender.setImage(UIImage(named: "radio_white"), for: UIControlState.normal)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DishTableViewCell", for: indexPath) as! DishTableViewCell
cell.titleLbl.text = self.menu[indexPath.section].menuOptions[indexPath.row].name
if self.menu[indexPath.section]. menuOptions[0].title == "checkbox" {
cell.checkboxBtn.isHidden = false
cell.radioBtn.isHidden = true
}else if
self.menu[indexPath.section]. menuOptions[0].title == "radio" {
cell.checkboxBtn.isHidden = true
cell.radioBtn.isHidden = false
}
cell.checkboxBtn.addTarget(self, action: #selector(DishViewController.checkboxSelected(_:)), for: UIControlEvents.touchUpInside)
cell.radioBtn.tag = self.menu[indexPath.section]. menuOptions[indexPath.row].id
cell.radioBtn.addTarget(self, action: #selector(DishViewController.radioBtnSelected(_:)), for: UIControlEvents.touchUpInside)
return cell
}
Or is there any other way to do other than using tableview? Please assist. Thank you

I would suggest you to use UIImageView instead of button and use tableView didSelectRowAt method.
I have edited your code and made some changes below:
1.Declare two variables for keeping track of indexpath
var radioButtonIndexPath = [Int:IndexPath]() //for radiobutton
var checkboxIndexPath = [indexPath]() //for checkbox
2.cellForRowAt method has modified with an UIImageView instead of UIButton
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "DishTableViewCell", for: indexPath) as! DishTableViewCell
cell.titleLbl.text = self.menu[indexPath.section].menuOptions[indexPath.row].name
if self.menu[indexPath.section]. menuOptions[0].title == "checkbox" {
cell.checkbox.isHidden = false
cell.radio.isHidden = true
if checkboxIndexPath.contains(IndexPath) {
checkbox.image = UIImage(named:"red_checkbox")
}else{
checkbox.image = UIImage(named:"checkbox2")
}
}else if
self.menu[indexPath.section]. menuOptions[0].title == "radio" {
cell.checkbox.isHidden = true
cell.radio.isHidden = false
if radioButtonIndexPath.keys.contains(indexPath.section) {
if radioButtonIndexPath[indexPath.section] == indexPath {
radio.image = UIImage(named:"radio_red")
}else{
radio.image = UIImage(named:"radio_white")
}
}else{
radio.image = UIImage(named:"radio_white")
}
}
return cell
}
3.didSelectRowAt method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if self.menu[indexPath.section].menuOptions[0].title == "checkbox" {
if checkboxIndexPath.contains(IndexPath) {
checkboxIndexPath.remove(at: checkboxIndexPath.index(of: IndexPath))
}else{
checkboxIndexPath.append(IndexPath)
}
}else if self.menu[indexPath.section].menuOptions[0].title == "radio" {
radioButtonIndexPath[indexPath.section] = indexPath
}
yourtableView.reloadData() // reload your tableview here
}
try this code and let me know if there any issue.

Related

Issue while Expanding and Collapse of Table View Cell

i have a button and some content in a cell. Now when i tap a button i want the cell to expand and collapse, which is working fine for cell whose button is tapped. Now issue is coming that when i have expanded one cell and try to expand another cell while keeping first cell expanded it close first the previous cell and than expand my second cell. I want to expand and collapse the cells parallel and can collapse any cell rather than to collapse first. I'm changing height of cell on click of button. This is my code for expanding and collapsing,
extension ActiveConsultantDetailVC : UITableViewDelegate, UITableViewDataSource
{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0
{
return 100
}
else if indexPath.row == 1
{
return 80
}
else if indexPath.row == 2
{
if (flag == true && indexPath.row == indexValue)
{
return UITableView.automaticDimension
}
else
{
return 40
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
else if indexPath.row == 2
{
let cell = consultantTableView.dequeueReusableCell(withIdentifier: "descriptionCell", for: indexPath) as! ConsultDescriptionTVC
indexDescription = indexPath
cell.expandBtn.addTarget(self, action: #selector(expandDescriptionView), for: .touchUpInside )
cell.selectionStyle = .none
return cell
}
}
#objc func expandDescriptionView()
{
let cell = consultantTableView.cellForRow(at: indexDescription) as! ConsultDescriptionTVC
indexValue = indexDescription.row
if flag && indexValue == indexDescription.row{
//statesDetailTableView.beginUpdates()
self.consultantTableView.reloadRows(at: [indexDescription], with: UITableView.RowAnimation.none)
// statesDetailTableView.endUpdates()
cell.expandBtn.setTitle("-", for: .normal)
flag = false
}
else{
//statesDetailTableView.beginUpdates()
self.consultantTableView.reloadRows(at: [indexDescription], with: UITableView.RowAnimation.none)
// statesDetailTableView.endUpdates()
cell.expandBtn.setTitle("+", for: .normal)
flag = true
}
}
As you can see when i tap it call a function that then expand height of cell. My UI Looks like this,
You just need to create 2 Cells in UITableView(In Storyboard). First cell for those who are not expandable and Second cell for the expandable.(In this case, now you don't need to change the Height of the CELL)
class TableVC: UITableViewController {
// MARK:- Constants And Vars
var isCellNAME1Expanded = false
var isCellNAME2Expanded = false
}
class TableVC: UITableViewDataSource, UITableViewDelegate {
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "simpleCell", for: indexPath) as! SideMenuBasicTableViewCell
switch indexPath.row {
case 0:
cell.itemName.text = "HOME"
break
case 1:
cell.itemName.text = "About Us"
break
case 2:
if(isCellNAME1Expanded){
//expandedCell
let cell = tableView.dequeueReusableCell(withIdentifier: "expandedCell", for: indexPath) as! SideMenuBasicTableViewCell
cell.itemName.text = "Expanded- CEll1"
return cell
}else{
cell.arrowDownImageView.isHidden = false
cell.itemName.text ="Expanded- CEll1"
}
break
case 3:
if(isCellNAME2Expanded){
//expandedCell
let cell = tableView.dequeueReusableCell(withIdentifier: "expandedCell", for: indexPath) as! SideMenuBasicTableViewCell
cell.itemName.text = "Expanded- CEll2"
return cell
}else{
cell.arrowDownImageView.isHidden = false
cell.itemName.text = "Expanded- CEll2"
}
break
case 4:
cell.itemName.text = "Portfolio"
break
case 5:
cell.itemName.text = "Contacts8"
break
default:
break
}
return cell
}
//And in `DidSelectRow` Method
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if(indexPath.row == 2){
if(isCellNAME1Expanded){
isCellNAME1Expanded = false
tableView.reloadRows(at: [indexPath], with: .none)
}else{
isCellNAME1Expanded = true
tableView.reloadRows(at: [indexPath], with: .none)
}
}if(indexPath.row == 3){
if(isCellNAME2Expanded){
isCellNAME2Expanded = false
tableView.reloadRows(at: [indexPath], with: .none)
}else{
isCellNAME2Expanded = true
tableView.reloadRows(at: [indexPath], with: .none)
}
}else if(indexPath.row == 0){
// Handle it yourself
}else if(indexPath.row == 1){
// Handle it yourself
}else if(indexPath.row == 4){
// Handle it yourself
}else if(indexPath.row == 5){
// Handle it yourself
}
}
}

How to select only tapped cells with button?

Good time of day, in my app i have a tableview and custom cell, in cell there are labels, button and progressBar, so that when i tap button download proceeds and progressBar shows progress, but when i scroll down i realise that there are other cells are selected and shows progress and when i scroll up again progress of my selected cell stops. Could you help, any feedbacks appreciated )
That's my TableViewController :
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return titles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ViewControllerTableViewCell
cell.pesnya.text = titles[indexPath.row]
cell.pevets.text = artists[indexPath.row]
cell.url = urls[indexPath.row]
return (cell)
}
#IBAction func buttonPressed(_ sender: AnyObject) {
(sender as! UIButton).isSelected = !(sender as! UIButton).isSelected
if (sender as! UIButton).isSelected {
if let indexPath = tableView.indexPath(for: sender.superview!?.superview as! UITableViewCell) {
DownloadManager.shared.download(url: urls[indexPath.row], title: titles[indexPath.row])
}
} else {
// (sender as! UIButton).setTitle("Удалить", for: UIControlState.normal)
if let indexPath = tableView.indexPath(for: sender.superview!?.superview as! UITableViewCell) {
let name = "\(titles[indexPath.row]).mp3"
let name2 = name.replacingOccurrences(of: " ", with: "")
let filePathURL = URL(string:"string")
do {
try FileManager.default.removeItem(at: filePathURL!)
} catch {
print("Could not delete file: \(error)")
}
}
}
}
This is because table view cell will be reused when you scroll. Set views to initial status in cell's prepareForReuse. Like this:
class ACell: UITableViewCell {
override func prepareForReuse() {
view.hidden = true
}
}
try this :-
override func tableView(_ tableView: UITableView, numberOfRowsInSection
section: Int) -> Int
{
return titles.count
}
override func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for:
indexPath) as! ViewControllerTableViewCell
cell.pesnya.text = titles[indexPath.row]
cell.pevets.text = artists[indexPath.row]
cell.url = urls[indexPath.row]
// Create IBOutlet for button
cell.btnPressed.tag = indexPath.row
cell.btnPressed.addTarget(self, action: #selector(self.buttonPressed(sender:)), for: .touchUpInside)
if cell.btnPressed.isSelected {
DownloadManager.shared.download(url: urls[indexPath.row], title: titles[indexPath.row])
}else {
let name = "\(titles[indexPath.row]).mp3"
let name2 = name.replacingOccurrences(of: " ", with: "")
let filePathURL = URL(string:"string")
do {
try FileManager.default.removeItem(at: filePathURL!)
} catch {
print("Could not delete file: \(error)")
}
}
return (cell)
}
#IBAction func buttonPressed(_ sender: UIButton) {
let indexPath = IndexPath(row: sender.tag, section: 0)
if let cell = tableView.cellForRow(at: indexPath) as?
ViewControllerTableViewCell {
cell.btnPressed.isSelected = !cell.btnPressed.isSelected
tableview.reloadData()
}
}

Swift 3: how to change the button image in UITableView?

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
let doLikeBtn = detailCell.contentView.viewWithTag(106) as! UIButton
doLikeBtn.setTitle(String(indexPath.row), for: UIControlState.normal)
doLikeBtn.addTarget(self, action: #selector(doLike(sender:)), for: UIControlEvents.touchUpInside)
...
}
...
func doLike(sender: UIButton) {
selectUserIndex = sender.title(for: UIControlState.normal)!
if sender.image(for: UIControlState.normal) == UIImage(named: "btn_Like.png") {
sender.setImage(UIImage(named: "btn_not_Like.png"), for: UIControlState.normal)
}else {
sender.setImage(UIImage(named: "btn_Like.png"), for: UIControlState.normal)
}
}
If I click one button in table, and then change the image of button, but other some button's images also are changed repeatedly.
I can't understand this.
Please help me!
Best regards.
a UITableViewCell object is reusable—that is for performance reasons,
you should only reset attributes of the cell that are not related to
content.
So you need to update your cell every time cellForRowAt invoked:
also you need to keep track of which cell getting "liked or dislike" so you can add for example a boolean isLike property to YourCustomCell
and toggle it true or false upon doLike func triggered
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.deque... as! YourCustomCell
if cell.isLiked == false {
cell.btnSomething.setImage(UIImage(named: "btn_not_Like.png"), for: UIControlState.normal)
}else {
cell.btnSomething.setImage(UIImage(named: "btn_Like.png"), for: UIControlState.normal)
}
}
Updated for Swift 3/4:
Use lines of code to change/set the UIButton Image in UITableView;
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "YourTableViewCellStoryboardIdentifier")! as! YourTableViewCellName
if status == 1{
cell.mStatusButtonOutlet.setImage(UIImage(named: "pending_icon"), for: .normal)
}else if status == 2{
cell.mStatusButtonOutlet.setImage(UIImage(named: "approval"), for: .normal)
}else if status == 3{
cell.mStatusButtonOutlet.setImage(UIImage(named: "reject_icon"), for: .normal)
}else{
cell.mStatusButtonOutlet.setImage(UIImage(named: "cancle"), for: .normal)
}
return cell
}
Note: where mStatusButtonOutlet - is your UIButtonOutlet
Enjoy..!
In Swift 4, Below is a simple way to do it
class AddressTableViewCell: UITableViewCell {
#IBOutlet weak var starButton: UIButton!
var buttonObject : (() -> Void)? = nil
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
#IBAction func buttonPressed(sender: UIButton) {
if let buttonAction = self.buttonObject {
buttonAction()
}
}
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "addressTableViewCell", for: indexPath) as! AddressTableViewCell
cell.buttonObject = {
if cell.starButton.currentImage == UIImage(named: "fav") {
cell.starButton.setImage(UIImage(named: "favYellow"), for: .normal)
} else {
cell.starButton.setImage(UIImage(named: "fav"), for: .normal)
}
}
return cell
}

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

I have a xib view in which I took a tableView with a customcell xib. In this custom cell I have a checkbox button which behaves like check and uncheck using custom cell. But when ever I click the first cell checkbox as tick the multiple of 9th cell like 9th row cell, 18th row cell, .....also became ticked. and while scrolling the checkbox tick option is changing between cells. I am not able to know why this is happening..??
I have registered cell xib view as:
override func viewDidLoad() {
super.viewDidLoad()
//Register custom cell
let nib = UINib(nibName: "CustomOneCell", bundle: nil)
AddOnTableView.registerNib(nib, forCellReuseIdentifier: "addoncell")
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return ADDONITEMS.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:CustomOneCell = AddOnTableView.dequeueReusableCellWithIdentifier("addoncell") as! CustomOneCell
let item: AddOnItems = ADDONITEMS[indexPath.row]
cell.addOnName.text = item.name
cell.addOnPrice.text = "£\(item.price!)"
return cell
}
For checkbox I have added a custom class as below:
var isCheckedAddOnGlobal = Bool()
class AddOnCheckBox: UIButton {
let checkedImage = UIImage(named: "checkboxredtick.png")! as UIImage
let unCheckedImage = UIImage(named:"checkbox untick.png")!as UIImage
//bool property
var ischecked:Bool = false{
didSet{
//print(ischecked)
if ischecked == true{
self.setImage(checkedImage, forState: .Normal)
}else{
self.setImage(unCheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action:#selector(CheckBox.buttonClicked(_:)), forControlEvents: UIControlEvents.TouchUpInside)
self.ischecked = false
}
func buttonClicked(sender: UIButton) {
if (sender == self) {
if ischecked == true{
ischecked = false
isCheckedAddOnGlobal = false
}else{
ischecked = true
isCheckedAddOnGlobal = true
}
}
}
}
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()
}
Best way is on selecting cell call
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath) as! CustomOneCell
cell.buttonClicked()
}
and change buttonClicked method to
func buttonClicked() {
...
}
I would make an object, which contains the product information and a boolean, to check if the product has been selected or not.
If you make it this way, the checkmarks will appear correct. When you are scrolling on a tableview, then it loads the data everytime it shows new cells.
Right now, it only knows that the index etc. 9 is selected, and when you scroll down and load new cells, then the index 9 will be selected automatic again.
Try something like this:
Example
class Product {
var productName = "Test"
var isSelected: Bool = false
}
Under your cellForRowAtIndexPath
if product.isSelected == true {
cell.checkBoxBtn.setImage(UIImage(named:"checkbox untick.png"), forState: .Normal)
} else {
cell.checkBoxBtn.setImage(UIImage(named: "checkboxredtick.png"), forState: .Normal)
}

UITableview with custom cells

Hi I am having a problem in my app
I am using UITableView with the custom cell. In each cell, I have a
checkbox, when I check it, it is getting the cell’s element into a
set. Everything is working correctly except a small problem.
The Problem:
2
As in the image when I click on the first row, the 10th row is seeming
like checked. The image changed to checked but in practice, it did
not occur in my set.
Here is the related part of the code
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell") as! TblCell
cell.labelInCell.text = tableData[indexPath.row]//data.myFunc().myset[indexPath.row]
cell.checkBoxInCell.tag = indexPath.row
cell.checkBoxInCell.addTarget(self, action: Selector("yourCheckBoxClicked:"), forControlEvents: .TouchUpInside)
// cell.images.image = UIImage(named: tableData[indexPath.row])
// if images name is same as in tableData put it in front of label
return cell
}
func yourCheckBoxClicked(cbx:UIButton){
let picked = self.tableData[cbx.tag]
if choosenSet.contains(picked) {
choosenSet.remove(picked) // uncheck
} else {
choosenSet.insert(picked) // check
}
print(choosenSet)
}
Number of rows in section ->
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tableData.count
}
Checkboxes class ->
class checkBox: UIButton {
//images
let checkedImage = UIImage(named: "button_checkboxFilled")
let uncheckedImage = UIImage(named: "button_checkboxEmpty")
// Bool Property
var isChecked : Bool = false {
didSet{
if isChecked == true{
self.setImage(checkedImage, forState: .Normal)
}else {
self.setImage(uncheckedImage, forState: .Normal)
}
}
}
override func awakeFromNib() {
self.addTarget(self, action: "buttonClicked:" , forControlEvents: UIControlEvents.TouchUpInside)
self.isChecked = false
}
func buttonClicked (sender:UIButton) {
if (sender == self) {
if isChecked == true {
isChecked = false
}else {
isChecked = true
}
}
}
}
This is because of cells reusage. You should just add this line to your func tableView(tableView:cellForRowAtIndexPath:) method:
cell.checkBoxInCell.isChecked = choosenSet.contains(tableData[indexPath.row])
Your cells are reused, quick solution which will work fine as your table view doesn't have a lot of cells:
let cell: TblCell = self.tableView.dequeueReusableCellWithIdentifier("cell" + String(indexPath.row)) as! TblCell
If your tableView has many rows than you should have "cell" identifier and update the selection style on each cellForRowAtIndexPath call