Button state activates on wrong cells - swift

I added button into cell-s and added action so if user touches it then the state is "Dislike" and if user touches again the state is "Like". However, the state applies to other cell buttons also. And if I scroll fast it just randomly picks what cell button should have the state. What causes this?
I call button with function inside cellForRowAt indexPath: IndexPath function like this:
cell.likeButton.addTarget(self, action: #selector(like), for: .touchUpInside)
And this is the function that is assigned to the button:
func like(sender: UIButton){
let section = 0
let row = sender.tag
let indexPath = IndexPath(row: row, section: section)
let cell: FeedTableViewCell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedTableViewCell
FIRDatabase.database().reference().child("posts").child(postsArray[indexPath.row].key).runTransactionBlock({ (currentData: FIRMutableData) -> FIRTransactionResult in
if var post = currentData.value as? [String : AnyObject], let uid = FIRAuth.auth()?.currentUser?.uid {
var stars : Dictionary<String, Bool>
stars = post["stars"] as? [String : Bool] ?? [:]
var starCount = post["starCount"] as? Int ?? 0
if let _ = stars[uid] {
// Unstar the post and remove self from stars
starCount -= 1
stars.removeValue(forKey: uid)
cell.likeButton.tag = indexPath.row
cell.likeButton.setTitle("Like", for: .normal)
cell.likeLabel.text = "\(starCount)"
} else {
// Star the post and add self to stars
starCount += 1
stars[uid] = true
cell.likeButton.tag = indexPath.row
cell.likeButton.setTitle("Dislike", for: .normal)
cell.likeLabel.text = "\(starCount)"
}
post["starCount"] = starCount as AnyObject?
post["stars"] = stars as AnyObject?
// Set value and report transaction success
currentData.value = post
return FIRTransactionResult.success(withValue: currentData)
}
return FIRTransactionResult.success(withValue: currentData)
}) { (error, committed, snapshot) in
if let error = error {
print(error.localizedDescription)
}
}
}
And like this I created the tableview with cells:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: FeedTableViewCell = tableView.dequeueReusableCell(withIdentifier: "feedCell", for: indexPath) as! FeedTableViewCell
cell.likeButton.tag = indexPath.row
cell.likeButton.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
}
What causes the state to transfer to the other buttons also? I even added tags so it detects the selected button. Is there something to do with cell reuse?
It adds likes to Firebase to the right one..

This is caused by reusing previous cells when scrolling and is the base mechanism of a table view.
You need to reset the state of your button on every call to cellForRowAtIndexPath.
Between let cell = ... and cell.starButton.addTarget you need to perform something like cell.starButton.deselect(), or .select(), based on the index path you're working on.

var selectindex : Int?
var selectedindex : NSMutableArray = NSMutableArray()
#IBOutlet var tableview: UITableView!
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("LikeCell", forIndexPath: indexPath)
let like: UIButton = (cell.viewWithTag(2) as! UIButton)
let comment: UIButton = (cell.viewWithTag(3) as! UIButton)
if selectedindex.containsObject(indexPath.row) {
like.setBackgroundImage(UIImage.init(named: "like.png"), forState: .Normal)
}else{
like.setBackgroundImage(UIImage.init(named: "like (1).png"), forState: .Normal)
}
comment.setBackgroundImage(UIImage(named: "chat.png"), forState: UIControlState.Normal)
like.addTarget(self, action: #selector(self.CloseMethod(_:event:)), forControlEvents: .TouchDown)
comment.addTarget(self, action: #selector(self.CloseMethod1(_:event:)), forControlEvents: .TouchDown)
return cell
}
#IBAction func CloseMethod(sender: UIButton, event: AnyObject) {
let touches = event.allTouches()!
let touch = touches.first!
let currentTouchPosition = touch.locationInView(self.tableview)
let indexPath = self.tableview.indexPathForRowAtPoint(currentTouchPosition)!
selectindex = indexPath.row
if selectedindex.containsObject(selectindex!) {
selectedindex.removeObject(selectindex!)
// call your firebase method for update database
}else{
selectedindex.addObject(selectindex!)
// call your firebase method for update database
}
self.tableview.reloadData()
}
Output :
https://www.dropbox.com/s/ub7wln5y6hdw0sz/like%20button.mov?dl=0

I think this issue is because of dequeuing your cell twice. you should try;
func like(sender: UIButton){
//your code ...
let cell: FeedTableViewCell = self.tableViewAddress.cellForRowAtIndexPath(indexPath) as! FeedTableViewCell
//Your code ...

Related

How to parse text from each cell to another View Controller UITextField

I have a class which is called InvoicesViewController "first image"
in that class there are cells that come from the API and each cell has a Label which represents a price of an item and a button near that price which segues you to PayViewController "second image"
and now i want that the UITextField in the PayViewController "second image" to be filled with the selected cell's label price.
I hope I am clear and someone guides me to the appropriate answer since i can't wrap my head around this one :)
This is my Code of the tableView :
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
let data = notifications.Result![changeCustomerKey.DefaultsKeys.keyTwo].properties?[0].invoices
return data?.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "invoicesCell", for: indexPath) as? invoicesModel else { return UITableViewCell() }
let currentNotifications = notifications.Result![changeCustomerKey.DefaultsKeys.keyTwo].properties?[0].invoices
let currentInvoices = currentNotifications![indexPath.row]
cell.mainPriceLabelInvoices.text = "€\(currentInvoices.priceWithVAT ?? 0.00)"
return cell
}
you can use navigation code for move to the next page and pass the value with navigation.
let storyBoard : UIStoryboard = UIStoryboard(name: "PayViewController", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "PayViewController") as! PayViewController
nextViewController.txtfield.text = value
self.present(nextViewController, animated:true, completion:nil)
At first, create a var in PayViewController like this
var price : Double = 0.0
Now in viewDidLoad() method write down the below code
self.textField.text = "€\(price)"
then, you have to write the following code in cellForRowAt func of tableView in InvoicesViewController
cell.editBtn.addTarget(self, action: #selector(buttonPayTapped(_:)), for: .touchUpInside)
Now add the following function
#objc func buttonPayTapped(_ sender:UIButton) {
guard let indexPath = tableView.indexPathForView(sender) else {return }
let vc = self.storyBoard?.instantiateViewController(withIdentifier: "PayViewController") as! AddCategoriesVC
let currentNotifications = notifications.Result![changeCustomerKey.DefaultsKeys.keyTwo].properties?[0].invoices
let currentInvoices = currentNotifications![indexPath.row]
// This is to transfer the selcted Data to Payment page
vc.price = currentInvoices.priceWithVAT ?? 0.00
self.present(vc, animated:true, completion:nil)
}
Add the following extension to get the exact indexPath
extension UITableView {
func indexPathForView(_ view: UIView) -> IndexPath? {
let center = view.center
let viewCenter = convert(center, from: view.superview)
let indexPath = indexPathForRow(at: viewCenter)
return indexPath
}
}

UITapGestureRecognizer has no member tag

In this case i need my uiLabel clickable, so i add uiTapRecognizer. Then, i need to send the indexpath section to the tag so i can get the choosen section.
But when i try to get the sender.tag in tapFunction() there is error : UITapGestureRecognizer has no member tag
If i change the sender to uiButton -> func tapFunction(_ sender:uiButton) {
it doesn't print anything inside the function
Is there any workaround for this case (still using uilabel not change it to uibutton)?
Can i also send data when #selector tapfunciton(_: , data:"text")
and read it in tapFunction(data:String)? If this possible, can someone show it ?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "LoanCell", for: indexPath) as? LoanCell else {
return UITableViewCell()
}
let item = viewModel.itemsForRowAt(indexPath: indexPath)
let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapFunction(_:)))
cell.syaratLabel.tag = indexPath.section
cell.isUserInteractionEnabled = true
cell.syaratLabel.addGestureRecognizer(tap)
return cell
}
#objc
func tapFunction(_ sender:UITapGestureRecognizer) {
print("successprint")
let item = viewModel.itemsForRowAt(indexPath: IndexPath(row: 0, section: sender.tag))
let viewController = DetilSandKViewController(messageSnK:item.prdAgreementDetail, titleSnK:item.nomorKontrak)
viewController.modalPresentationStyle = .overCurrentContext
viewController.modalTransitionStyle = .crossDissolve
present(viewController, animated: true, completion: nil)
}

Invalid UILabel content when clicking on a cell

I have an UITableView with custom cells:
class customChatCell: UITableViewCell {
#IBOutlet weak var userName: UILabel!
#IBOutlet weak var userMessage: UILabel!
And I have a code for open URL contained in the userMessage:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "ClientCell"
self.cell = self.tableView.dequeueReusableCell(withIdentifier: identifier) as? customChatCell
self.cell.selectionStyle = UITableViewCell.SelectionStyle.none
// Unpack message from Firebase DataSnapshot
let messageSnapshot = self.messages[indexPath.row]
guard let message = messageSnapshot.value as? [String: String] else { return cell }
let name = message[Constants.MessageFields.name] ?? ""
if let imageURL = message[Constants.MessageFields.imageURL] {
self.cell.userName.text = name
self.cell.hiddenLabel.isHidden = true
self.cell.userMessage.text = imageURL
let tap = UITapGestureRecognizer(target: self, action: #selector(FCViewController.tapFunction))
self.cell.addGestureRecognizer(tap)
if self.cell.userName.text == Auth.auth().currentUser?.displayName {
self.cell.userName.textAlignment = .right
self.cell.userMessage.textAlignment = .right
self.cell.hiddenLabel.textAlignment = .right
} else {
self.cell.userName.textAlignment = .left
self.cell.userMessage.textAlignment = .left
self.cell.hiddenLabel.textAlignment = .left
}
self.cell.setNeedsLayout()
} else {
let text = message[Constants.MessageFields.text] ?? ""
self.cell.userName.text = name
self.cell.userMessage.text = text
self.cell.hiddenLabel.isHidden = true
self.cell.hiddenLabel.isUserInteractionEnabled = false
if self.cell.userName.text == Auth.auth().currentUser?.displayName {
self.cell.userName.textAlignment = .right
self.cell.userMessage.textAlignment = .right
} else {
self.cell.userName.textAlignment = .left
self.cell.userMessage.textAlignment = .left
}
}
return cell
}
#objc func tapFunction(sender:UITapGestureRecognizer) {
var url = URL(string: self.cell.userMessage.text!)!
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
But when clicking on any cell, only the link that is contained in the last cell always opens. No matter which cell I click.
How to make it so that when you click on a cell, the specific link that is contained in a particular userMessage opens?
You have to set Tag to each Cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "ClientCell"
self.cell = self.tableView.dequeueReusableCell(withIdentifier: identifier) as? customChatCell
...
self.cell.tag = indexPath.row // SETTING TAG TO EACH CELL. 0,1,2...
...
let tap = UITapGestureRecognizer(target: self, action: #selector(FCViewController.tapFunction))
self.cell.addGestureRecognizer(tap)
...
}
#objc func tapFunction(sender:UITapGestureRecognizer) {
print("CELL_TAG_NUMBER. ", sender.view.tag)
let messageSnapshot = self.messages[sender.view.tag] THIS WILL GIVE, 0,1,2...
print("Exact_URL. ", messageSnapshot)
//var url = URL(string: self.cell.userMessage.text!)!
//UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
No need to add a tap gesture to the cell, a UITableView has a delegate method for when a cell is selected. if you use that then you also don't need to track your cells using a tag(Not GOOD!), instead you will use the direct indexPath of that specific cell.
implement this method
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//Now you have your selected cell
let messageSnapshot = self.messages[indexPath.row]
print(messageSnapshot)
}

Select deselect the radio in uitableview section with array in ios swift

In tableview have different section.
Want to add the radio button for all the section.
Each section have individual select and deselect in tableview.
In first section choice1,[show in fig]
Selected cheese means cheese want to select, next if user click bacon means cheese automatically deselect.
[Here using radio button SSRadioButton class for click action. Create a radio button in tableview cell. how to write the button action for radio button. or suggest any new way].
Each radio button want individual select and deselect. The same process for all the section in tableview. how is possible help me. Thanks advance.
my code:
var radioControllerChoice : SSRadioButtonsController = SSRadioButtonsController()
var radioControllerDip : SSRadioButtonsController = SSRadioButtonsController()
func numberOfSections(in tableView: UITableView) -> Int {
return table_data.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return table_data[section].menu_id.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell:CustomiseTableViewCell = tableView.dequeueReusableCell(withIdentifier: "Customise") as! CustomiseTableViewCell
cell.name.text?=table_data[indexPath.section].menu_name[indexPath.row]
print(table_data[indexPath.section].customize[indexPath.row])
switch indexPath.section {
case 2:
radioControllerChoice.addButton(cell.radioBtn)
radioControllerChoice.shouldLetDeSelect = false
case 3:
radioControllerDip.addButton(cell.radioBtn)
radioControllerDip.shouldLetDeSelect = false
switch Int(table_data[indexPath.section].customize[indexPath.row]) {
case 1:
cell.radioBtn.isHidden = false
default:
print("Invalid choose")
cell.radioBtn.addTarget(self, action: #selector(ViewController.didSelectButton), for: .touchUpInside)
cell.radioBtn.tag = indexPath.row
}
}
}
func didSelectButton(selectedButton: UIButton?)
{
/// need solution for button action help me..
}
You can use UIButton instead of SSRadioButton, and then you can change the image of button for checked and unchecked radio button.
Swift3.2:
CustomiseTableViewCell
import UIKit
protocol CustomTableViewCellDelegate {
func didToggleRadioButton(_ indexPath: IndexPath)
}
class CustomiseTableViewCell: UITableViewCell {
#IBOutlet weak var itemLabel: UILabel!
#IBOutlet weak var radioButton: UIButton!
var delegate: CustomTableViewCellDelegate?
func initCellItem() {
let deselectedImage = UIImage(named: "ic_radio_button_unchecked_white")?.withRenderingMode(.alwaysTemplate)
let selectedImage = UIImage(named: "ic_radio_button_checked_white")?.withRenderingMode(.alwaysTemplate)
radioButton.setImage(deselectedImage, for: .normal)
radioButton.setImage(selectedImage, for: .selected)
radioButton.addTarget(self, action: #selector(self.radioButtonTapped), for: .touchUpInside)
}
func radioButtonTapped(_ radioButton: UIButton) {
print("radio button tapped")
let isSelected = !self.radioButton.isSelected
self.radioButton.isSelected = isSelected
if isSelected {
deselectOtherButton()
}
let tableView = self.superview as! UITableView
let tappedCellIndexPath = tableView.indexPath(for: self)!
delegate?.didToggleRadioButton(tappedCellIndexPath)
}
func deselectOtherButton() {
let tableView = self.superview?.superview as! UITableView
let tappedCellIndexPath = tableView.indexPath(for: self)!
let indexPaths = tableView.indexPathsForVisibleRows
for indexPath in indexPaths! {
if indexPath.row != tappedCellIndexPath.row && indexPath.section == tappedCellIndexPath.section {
let cell = tableView.cellForRow(at: IndexPath(row: indexPath.row, section: indexPath.section)) as! CustomiseTableViewCell
cell.radioButton.isSelected = false
}
}
}
}
Call initCellItem method from UITableViewDataSource's delegate method:
// Your ViewController
let menuList = [ ["Cheese", "Bacon", "Egg"],
["Fanta", "Lift", "Coke"] ] // Inside your ViewController
var selectedElement = [Int : String]()
func didToggleRadioButton(_ indexPath: IndexPath) {
let section = indexPath.section
let data = menuList[section][indexPath.row]
if let previousItem = selectedElement[section] {
if previousItem == data {
selectedElement.removeValue(forKey: section)
return
}
}
selectedElement.updateValue(data, forKey: section)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell:CustomiseTableViewCell =
tableView.dequeueReusableCell(withIdentifier: "Customise") as! CustomiseTableViewCell
let item = menuList[indexPath.section][indexPath.row]
cell.itemLabel.text = item
if item == selectedElement[indexPath.section] {
cell.radioButton.isSelected = true
} else {
cell.radioButton.isSelected = false
}
cell.initCellItem()
cell.delegate = self
// Your logic....
return cell
}
Alternate way:
You can use simple UIButton instead of any third party library (SSRadioButton) and use it like:
Set the UIButton's image in default state to - circle (as in the screenshot)
Set the UIButton's image in selected state to - filled circle
UIButton's action event can be captured in a normal way like you do in any other case.
Something like this:
Let me know if you want to follow this approach or need any kind of help regarding this.

How to Use Table View Cell Outside Table View Function

i need to change my image in cell using view will appear. But i can't use my cell in view will appear here what i've done
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
let cell: HomeCellTableViewCell = self.tableCity.dequeueReusableCell(withIdentifier: "Cell") as! HomeCellTableViewCell
if session == nil {
print("first")
cell.iconForDownload.setImage(UIImage(named: "ic_download"), for: .normal)
} else {
print("second")
cell.iconForDownload.setImage(UIImage(named: "ic_next_green"), for: .normal)
}
}
it print "first" but the image still didn't change
in my cellForRowAt :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: HomeCellTableViewCell = self.tableCity.dequeueReusableCell(withIdentifier: "Cell") as! HomeCellTableViewCell
let city = listData[indexPath.row]
cell.labelNameCity.text = city.region
cell.labelNameCityJpn.text = city.regionJpn
let stringImage = config.BASE_URL+city.imgArea
let url = URL(string: stringImage.replacingOccurrences(of: " ", with: "%20"))
urlDownload = config.BASE_URL+kota.urlDownload
urlDownloadFinal = URL(string: urlDownload.replacingOccurrences(of: " ", with: "%20"))
if session == nil {
cell.imageCity.kf.setImage(with: url)
cell.iconForDownload.setImage(UIImage(named: "ic_download"), for: .normal)
} else {
cell.imageCity.kf.setImage(with: url)
cell.iconForDownload.setImage(UIImage(named: "ic_next_green"), for: .normal)
}
return cell
}
You need to use cellForRow(at:) to get the cell it will return optional UITableViewCell so use if let or guard let to wrapped the optional.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
let indexPath = IndexPath(row: 0, section: 0) //Set your row and section
if let cell = tableView.cellForRow(at: indexPath) as? HomeCellTableViewCell {
//access cell
}
}
Note: The batter approach is to set your datasource array and simply reload the affected table rows using reloadRows(at:with:).
let indexPath = IndexPath(row: 0, section: 0) //Set your row and section
self.tableView.reloadRows(at: [indexPath], with: .automatic)