Accessing a UIButton in a custom UITableViewCell - ios

I am very puzzled.
In my cellForRowAtIndexPath method, I am trying to set a UIButton title:
Not working:
cell?.react?.setTitle("\(currentObject.likes)", forState: UIControlState.Normal)
where react is a UIButton, that is a subview of my UITableViewCell subclass.
I can't seem to update the title of my UIButton at all. The only way I have found to modify it is through the UITapGestureRecognizer in my UITableViewCell subclass,
Working:
func reaction(button : UIButton) {
if button.imageView?.image == UIImage(named: "ic_favorite.png") {
button.setImage(UIImage(named: "ic_favorite_border.png"), forState: UIControlState.Normal)
} else {
button.setImage(UIImage(named: "ic_favorite.png"), forState: UIControlState.Normal)
}
}

Create a custom cell (say myCell), add the button as an #IBOutlet, then type this let cell = tableView.dequeueReusableCellWithIdentifier("yourIdentifier", forIndexPath: indexPath) as! myCell.
Your problem I think is that you haven't linked the button to the cell & XCode doesn't know which button you are talking about.

You should first get a reference to your cell with cellForRowAtIndexPath passed to your method.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MyCellIdentifier", forIndexPath: indexPath) as! UIButton
// do anything with cell
return cell
}
Remember to set an identifier for your Table View Cell (UITableViewCell).

// try like this, first give tag for that button in your table cell
(cell?.contentView.viewWithTag(tag) as? UIButton)?.setTitle("\(currentObject.likes)", forState: UIControlState.Normal)

Related

Swift 3 adding selector issue on cell button

I have a custom button in my collection view cell. I just want to pass indexPath to it but I am getting
"unrecognized selector error"
Here is my code
cell.showMapButton.addTarget(self, action: #selector(testFunc(indexPath:)), for: .touchUpInside)
and the function is
func testFunc(indexPath: IndexPath){
print("Testing indexPath \(indexPath)")
}
If I remove the indexPath argument it works fine and the function gets called but I need that argument so please help me in resolving this issue.
In the addTarget(:action:for:) method for UIButton, the action can at most accept a single UIButton or any of it's superclass as parameter. If you need the indexPath of your button, you need to make it a property of your UIButton by subclass or other means. My way of doing it is to create a subclass of UIButton that have indexPath as it's property:
class ButtonWithIndexPath: UIButton {
var indexPath:IndexPath?
}
Then add target as normal:
cell.showMapButton.addTarget(self, action: #selector(testFunc(button:)), for: .touchUpInside)
Not forgetting to set the indexPath of your button to that of which ever cell it is in
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath) as! myCell
cell.button.indexPath = indexPath
...
return cell
}
And cast it into it's custom subclass in the function to read the indexPath:
func textFunc(button: UIButton) {
let currentButton = (button as! ButtonWithIndexPath)
print(currentButton.indexPath)
}
You can pass UIButton instance with target selector parameters for button action.
try with following code:
Add/replace below code, belongs to collection view cell into your collection view data source method - cellForRowAtIndexPath
cell.showMapButton.tag = indexPath.row
cell.showMapButton.addTarget(self, action: #selector(testFunc(button:)), for: .touchUpInside)
For Swift 4 - define your selector function using #objc, like below.
#objc func testFunc(button: UIBUtton){
print("Index = \(button.tag)")
}

Value of type '(UIButton) -> ()' has no member 'setTitle' in tableView

I have been creating an idle clicking game/application. The way you purchase upgrades is by clicking a button within a table cell. What I can't figure out, is how do I set a title for a button (I want to have price listed on each button). When I try I get the error, "Value of type '(UIButton) -> ()' has no member 'setTitle'" I'm using Xcode 8.2, and Swift 3.
Custom Cell Controller:
#IBAction func upgradePurchase(_ sender: UIButton) {
if GlobalVariables.sharedManager.balance >= GlobalVariables.sharedManager.UpgradesPrice[GlobalVariables.sharedManager.UpgradesCurrent] {
}
}
TableView Controller:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell
let row = indexPath.row
let showPrice = "Buy: $\(GlobalVariables.sharedManager.UpgradesPrice[row])"
cell.upgradeIdentifier.text = GlobalVariables.sharedManager.UpgradesName[row]
cell.upgradeDescription.text = GlobalVariables.sharedManager.UpgradesDesc[row]
cell.upgradePurchase.setTitle("\(showPrice)", for: .Normal)
GlobalVariables.sharedManager.UpgradesCurrent = GlobalVariables.sharedManager.UpgradesPrice[row]
return cell
}
By mistake or accidentally You are trying to set the title for button Action not to the UIButton instance. Here upgradePurchase is IBAction not the IBOutlet.
You need to set the title to your IBOutlet, so make one if it is not then set the title.
cell.btnupgradePurchase.setTitle("\(showPrice)", for: .normal)
Note: In Swift 3 it is .normal not .Normal.

Reusing cell doesn't work well - TableView

I have a problem about my cell's button.
In my tableView each row is composed by: an image, some labels and a button.
The button has a checkmark image. When it is clicked, the button's image changes.
The problem is that also another button's image changes without reason.
This mistake happens because my cell is reused.
I have tried to use prepareForReuse method in TableViewCell but nothing happens. I've also tried with selectedRowAt but I didn't have any results. Please help me.
Image 1:
Image 2:
This is my func in my custom Cell:
override func prepareForReuse() {
if checkStr == "uncheck"{
self.checkBook.setImage(uncheck, for: .normal)
} else if checkStr == "check"{
self.checkBook.setImage(check, for: .normal)
}
}
func isPressed(){
let uncheck = UIImage(named:"uncheck")
let check = UIImage(named: "check")
if self.checkBook.currentImage == uncheck{
checkStr == "check"
} else self.checkBook.currentImage == check{
checkStr == "uncheck"
}
}
In my tableView:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell: ListPropertyUserCell = tableView.cellForRow(at: indexPath) as! ListPropertyUserCell
let uncheck = UIImage(named:"uncheck")
let check = UIImage(named: "check")
if selectedCell.checkBook.imageView?.image == uncheck{
selectedCell.checkStr = "check"
} else if selectedCell.checkBook.imageView?.image == check{
selectedCell.checkStr = "uncheck"
}
}
From the information in your post, this looks like a cell reuse issue. The problem is that the tableView reuses the cells rather than creating new ones, to maintain performance. If you haven't reset the cell's state, the reused cell will be remain configured in the old state.
For a quick fix, you can implement the prepareForReuse method on UITableViewCell.
However, you'll need to store which cell is 'checked' in your view controller if you want the checkbox to be selected after scrolling the tableView. You can store this yourself, or use the tableView's didSelectRowAtIndexPath method.
Try to do it like this:
var checkBook = UIImageView()
if self.checkBook.image == UIImage(named: "check"){
self.checkBook.image = UIImage(named: "uncheck")
}
else{
self.checkBook.image = UIImage(named: "check")
}
If you're using the click on the entire cell, you can override the setSelected func in your custom cell just like that.
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
if selected {
self.checkBook.image = UIImage(named: "check")
} else {
self.checkBook.image = UIImage(named: "uncheck")
}
}
UITableViewCell is reusable. You can't store state of view in cell. You should setup cell in
func tableView(UITableView, cellForRowAt: IndexPath)
method of your data source
The easiest way to achieve that is to implement
func tableView(UITableView, didSelectRowAt: IndexPath)
func tableView(UITableView, didDeselectRowAt: IndexPath)
methods of UITableViewDelegate
Then you can add/remove indexPath to some array in these methods and in cellForRowAtIndexPath setup cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("YourTableViewCell") as! YourTableViewCell
if array.contains(indexPath) {
cell.checkBook.image = UIImage(named: "check")
} else {
cell.checkBook.image = UIImage(named: "uncheck")
}
return cell
}
Try my code . here selectindex is use for get selected cell index and selectedindex is NSMutableArray that i store all selected cell value.
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)// like button
let comment: UIButton = (cell.viewWithTag(3) as! UIButton) // comment button
comment.setBackgroundImage(UIImage(named: "chat.png"), forState: UIControlState.Normal) // comment button set
like.addTarget(self, action: #selector(self.CloseMethod(_:event:)), forControlEvents: .TouchDown)
comment.addTarget(self, action: #selector(self.CloseMethod1(_:event:)), forControlEvents: .TouchDown)
return cell
}
// This is my like button action method.
#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!) {
sender.setBackgroundImage(UIImage.init(named: "like (1).png"), forState: .Normal)
selectedindex.removeObject(selectindex!)
}else{
sender.setBackgroundImage(UIImage.init(named: "like.png"), forState: .Normal)
selectedindex.addObject(selectindex!)
}
}
I faced this problem recently, and did not find much about it. What solve, after much searching, was to use:
override func prepareForReuse() {
btnAdd.setImage(nil, for: .normal) //here I use to change to none image
super.prepareForReuse()
}
just put this method inside your custom UITableViewCell, and set which item you want to realese stats.

How to tap buttons or images without selecting the whole cell with Swift?

I'm using a custom TableViewCell in my iOS app. I use the method tableViewDidSelectRowAtIndexPath to open a new ViewController. What I need to do is to add a button or an image somewhere in the custom cell so if I tap the button or whatever element don't open the ViewController, but execute a function without opening the cell.
set [cell.button setTag:indexPath.row] in cellForRowAtIndexPath method.
and than addTarget to cell.button like
[cell.button addTarget:self action:#selector(yourAction:) forControlEvents:UIControlEventTouchUpInside]];
and than do Whatever you want to do in yourAction
with getting tag from sender.
Or you want code for that than please add your code what you had done so we can help more if you are new in iOS.
This code may helps you
here i have used custom buttom in table and add target to that buton
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: AnyObject = tableView.dequeueReusableCellWithIdentifier("CellIdentifier", forIndexPath: indexPath)
// use your custom cell here
//cell = UIColor.redColor()
//cell.textLabel?!.text = String(data[indexPath.row])
//nameTextField.text = ""
let custom_btn : UIButton? = UIButton.init(type: .System)
//declaring custom button
custom_btn?.setTitle("custom button", forState: .Normal)
custom_btn!.tag = indexPath.row
custom_btn!.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
cell .addSubview(custom_btn!);
return cell as! UITableViewCell
}
func buttonClicked(sender:UIButton)
{
if(sender.tag == 5){
//Do something for tag
}
print("hello")
}

Changing a single UIButton image in a collection view is affecting multiple cells

I currently am using a Collection View to display a list of events to a user, and each one of my custom cells has a button that invites the user to attend the event. When pressed, the button image should then change to a newImage.png which displays that they are now attending that event. When I do this in my code below, pressing the button does in fact change the picture, but as I scroll down my collection view, multiple cells that have yet to be clicked also have changed to the "newImage.png." How can I stop this from happening?
class CustomCell: UICollectionViewCell{
#IBAction func myButtonAction(sender: UIButton) {
myButtonOutlet.setImage(UIImage(named: "newImage.png"), forState: UIControlState.Normal)
}
#IBOutlet weak var myButtonOutlet: UIButton!
}
The collection view is reusing cells, as it is designed to do. What you should do is reset the image in your cellForItemAtIndexPath implementation.
I've had this issue before too and this is most likely do to cell reuse. What you might try to do to avoid this problem is to explicitly set the cell's image in your cellForItemAtIndexPath() and then add something to your model that keeps track of which events the user is attending. Then, again in your cellForItemAtIndexPath(), check the model to see what button should be on that cell, and then change it accordingly.
You need to store selected button index in your class and check perticular index in your function
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! UICollectionViewCell
cell.backgroundColor = UIColor.blackColor()
if(selectedIndex == indexPath.row){
myButtonOutlet.setImage(UIImage(named: "newImage.png"), forState: UIControlState.Normal)
//change background image here also your button code
}
return cell
}
and after doing this steps . Reload collection view .
This ended up solving my problem. I have an array that I store my Cells in. Each cell has a boolean called isAttending. In my cellForItemAtIndexPath method, I implemented the code below along with the function switchImage:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell = collectionView.dequeueReusableCellWithReuseIdentifier("CalendarCell", forIndexPath: indexPath) as! CalendarCell
cell.customButton.layer.setValue(indexPath.row, forKey: "index")
cell.customButton.addTarget(self, action: "switchImage:", forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
func switchImage(sender: UIButton){
let index : Int = (sender.layer.valueForKey("index")) as! Int
if (events[index].isAttending == false){
events[index].isAttending = true
cell.customButton.setImage(UIImage(named: "isAttending.png"), forState: UIControlState.Normal)
}else{
events[index].isAttending = false
cell.customButton.setImage(UIImage(named: "isNotAttending.png"), forState: UIControlState.Normal)
}
self.collectionView.reloadData()
}

Resources