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

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
}

Related

How to disable/enable notifications in tablewiew by using UISwitch in Swift

I am working on a location-based reminder app. I show all reminders that user created on a table view. I have also UISwitch on every cell. I want that UISwitch disables/enables reminders individually, not all notifications. I couldn't figure it out.
extension MainViewController: UITableViewDataSource{
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.items?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
//switch
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = indexPath.row
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
cell.accessoryView = swicthView
let itemToRemove = self.items![indexPath.row]
let notifToRemove: String = itemToRemove.notifID!
return cell
}
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
}
I believe your code is not working because cells are reused and actions on specific cells should be handled from the cell class rather than from ViewController. Move this code into the UITableViewCell code.
let swicthView = UISwitch(frame: .zero)
swicthView.onTintColor = UIColor (named: "DingerBlue")
swicthView.setOn(true, animated: true)
swicthView.tag = switchViewTag!
swicthView.addTarget(self, action: #selector(self.SwitchBtn(_:)), for: .valueChanged)
#objc func switchDidChanged(_ sender: UISwitch){
print("Switch value is \(sender.isOn)")
if(sender.isOn){
print("on")
UIApplication.shared.registerForRemoteNotifications()
}
else{
print("Off")
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [notifToRemove])
}
}
and add a new property in the UITableViewCell
weak var switchViewTag: Int?
Modify your cellForRowAt delegate method to
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath)
let cell = tableView.dequeueReusableCell(withIdentifier: "itemCell", for: indexPath) as! itemTableViewCell
let item = self.items![indexPath.row]
cell.itemTitle?.text = item.itemName
cell.itemSubTitle?.text = item.itemDescription
cell.switchViewTag = indexPath.row
return cell
}

Change selected cell image uitableview

I want to change cell image when music is playing , I am using didselectrow for this when i select row it change image and when i scroll i see many other cell image is also changed i don't why please guide me
here is my code
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as? NaatListTableViewCell
cell?.btnPlayPause.setImage(UIImage(named: "pause"), for: .normal)
}
try:
var isPlayingIndex = -1
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as? NaatListTableViewCell
if indexPath.row == isPlayingIndex {
cell?.btnPlayPause.setImage(UIImage(named: "play"), for: .normal)
}else {
cell?.btnPlayPause.setImage(UIImage(named: "pause"), for: .normal)
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as? NaatListTableViewCell
if indexPath.row == isPlayingIndex {
cell?.btnPlayPause.setImage(UIImage(named: "pause"), for: .normal)
isPlayingIndex = -1
} else {
// stop current player
//
isPlayingIndex = indexPath.row
cell?.btnPlayPause.setImage(UIImage(named: "play"), for: .normal)
}
}
Instead of changing the image in didSelectRowAt method, you can do it another way by implementing setSelected(_:animated:) method in your custom UITableViewCell and handle the image status there, i.e.
override func setSelected(_ selected: Bool, animated: Bool) {
if selected {
btnPlayPause.setImage(UIImage(named: "pause"), for: .normal)
} else {
btnPlayPause.setImage(UIImage(named: "play"), for: .normal)
}
}
The above method handles the selection and de-selection for all the cells in the tableView in single-selection and multi-selection modes.
After this, you'll not need to write anything in tableView(_:didSelectRowAt:) method.
You need to maintain state for your cell manually. You might have some other data associated with your cell, from what you have asked it seems like a list of music files, you could associate with each music a unique id in its model. Now create a map of type var isPlaying = [Int: Bool]() (assuming you are going with int ids)
Then in didSelectRowAt set the value with corresponding id
self.isPlaying[YOUR_ID] = cell.isSelected
Now in your cellForRowAt method
var imageName = self.isPlaying[YOUR_ID] ? "play" : "pause"
cell?.btnPlayPause.setImage(UIImage(named: imageName), for: .normal)

TableView with multiple section and with radio button and checkbox button

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.

Disable tableview cell button on tap in swift

I have the button working correctly, I just can't figure out how to disable it on tap. I'm not sure if I can reference it from the addSomething(sender: UIButton) function like I reference the sender.tag.
Any idea? Thanks for any help.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! ExploreCell
// Configure the cell...
myCell.configureCell(teams[indexPath.row])
myCell.addSomethingButton.tag = indexPath.row
myCell.addSomethingButton.addTarget(self, action: #selector(self.addSomething), forControlEvents: .TouchUpInside)
myCell.addSomethingButton.enabled = true
//disable cell clicking
myCell.selectionStyle = UITableViewCellSelectionStyle.None
return myCell
}
What do you need to do is to store all tapped buttons in an array to check whether the button of this tag (current indexPath.row) has been tapped:
class ViewController: UIViewController {
var tappedButtonsTags = [Int]()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! ExploreCell
// Configure the cell...
myCell.configureCell(teams[indexPath.row])
myCell.addSomethingButton.tag = indexPath.row
// here is the check:
if tappedButtonsTags.contains(indexPath.row) {
myCell.addSomethingButton.enabled = false
} else {
myCell.addSomethingButton.addTarget(self, action: #selector(self.addSomething), forControlEvents: .TouchUpInside)
myCell.addSomethingButton.enabled = true
}
//disable cell clicking
myCell.selectionStyle = UITableViewCellSelectionStyle.None
return myCell
}
// I just Implemented this for demonstration purposes, you can merge this one with yours :)
func addSomething(button: UIButton) {
tappedButtonsTags.append(button.tag)
tableView.reloadData()
// ...
}
}
I Hope this helped.

UIbutton in UItableview with different actions for each button in each cell

In my UITableView I'm attempting to have a button that will have a different action for each cell i.e. links to different viewController, for each button but i've only managed to achieve it with one.
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return courseTitle.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
cell.image_view.image = courseImages[indexPath.row]
cell.title.text = courseTitle[indexPath.row]
cell.tableButton.tag = indexPath.row
cell.tableButton.addTarget(self, action: "Test", forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
#IBAction func Test(sender: AnyObject){
self.performSegueWithIdentifier("SuccessfulLogin", sender: nil)
}
If you have just few cells you can do it like so:
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
cell.image_view.image = courseImages[indexPath.row]
cell.title.text = courseTitle[indexPath.row]
cell.tableButton.tag = indexPath.row
switch indexPath.row {
case 0:
cell.tableButton.addTarget(self, action: "TestA", forControlEvents: UIControlEvents.TouchUpInside)
case 1:
cell.tableButton.addTarget(self, action: "TestB", forControlEvents: UIControlEvents.TouchUpInside)
case 2:
cell.tableButton.addTarget(self, action: "TestC", forControlEvents: UIControlEvents.TouchUpInside)
default:
cell.tableButton.addTarget(self, action: "TestD", forControlEvents: UIControlEvents.TouchUpInside)
}
return cell
}
func TestA(sender: AnyObject){ // no need for #IBAction as you set the target action while setting the cell
self.performSegueWithIdentifier("SuccessfulLogin", sender: nil)
}
func TestB(sender: AnyObject){
print("Second button action")
}
func TestC(sender: AnyObject){
print("Third button action")
}
func TestD(sender: AnyObject){
print("All other buttons action")
}
You can use that tag value for that and change your selector syntax also
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomCell
cell.image_view.image = courseImages[indexPath.row]
cell.title.text = courseTitle[indexPath.row]
cell.tableButton.tag = indexPath.row
cell.tableButton.addTarget(self, action: "test:", forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
#IBAction func test(sender: UIButton){
let row = sender.tag
switch(row) {
case 0:
self.performSegueWithIdentifier("Segue1", sender: nil)
case 1:
self.performSegueWithIdentifier("Segue2", sender: nil)
case 2:
self.performSegueWithIdentifier("Segue3", sender: nil)
default:
}
}