I have a button on a tableview cell.
When its pressed, at the moment all I want is to add a different image.
When its already in the function its already been pressed, so assigning an control event must be wrong. This doesn't work :
#IBAction func HeartPressed(sender: AnyObject) {
var heartImage : UIImage = UIImage(named: "HeartPink.png")!
sender.setImage(heartImage, forState: UIControlState.Selected)
}
Any idea how to get an image set on a button in a tableviewcell ?
It happen because button is overload one by one and in cell always last cell button change effect that you doing ..
so please do in this minor change in this function
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
in this function set button tag of all ..
button.tag = indexPath.row
and try again ..
Related
I am trying to implement play/pause button in tableview cell. each cell having single button, whenever user click it, It should change button image also need to call required function, also after scroll it should same.
Below code I am using
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("productCell") as ? SepetCell
cell.onButtonTapped = {
//Do whatever you want to do when the button is tapped here
}
See first of all every button of the tableView Cell will have a unique tag associated with it, so in order to update the button of a particular cell, you will have to define the tag of a button in the cells and then pass this tag to your function to perform action on that particular button of the selected cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_identifier", for:
indexPath) as! CellClass
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: #selector(playpause), for: .touchUpInside)
}
#objc func playpause(btn : UIButton){
if btn.currentImage == UIImage(named: "Play") {
btn.setImage(UIImage(named : "Pause"), forState: .Normal)
}
else {
btn.setImage(UIImage(named : "Play"), forState: .Normal)
}
// perform your desired action of the button over here
}
State of the art in Swift are callback closures. They are easy to implement and very efficient.
In the data source model add a property
var isPlaying = false
In Interface Builder select the button in the custom cell and press ⌥⌘4 to go to the Attributes Inspector. In the popup menu State Config select Default and choose the appropriate image from Image popup, Do the same for the Selected state.
In the custom cell add a callback property and an outlet and action for the button (connect both to the button). The image is set via the isSelected property.
#IBOutlet weak var button : UIButton!
var callback : (()->())?
#IBAction func push(_ sender: UIButton) {
callback?()
}
In the controller in cellForRow add the callback, item is the current item of the data source array. The state of the button is kept in isPlaying
cell.button.isSelected = item.isPlaying
cell.callback = {
item.isPlaying = !item.isPlaying
cell.button.isSelected = item.isPlaying
}
I have 2 VCs, one of them is called HomeVC the other is DetailVC. I have a table view on HomeVC which displays cells with a label and a button. DetailVC just has a label. I am displaying an array of strings on the table view and when the button on the cell is clicked i want to carry the text in the label to the DetailVC's label.
Now i can easily do this with either didSelectRowAt method or using indexPathForSelectedRow in prepare segue method. But both cases requires me to tap on the cell itself but not the button.
I am just a beginner in swift. But to explain this there shouldn't be need for much code. So if you can, please explain with detail.
Thanks in advance.
In cellForRowAt add target to button i.e
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellID", for: indexPath)
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: Selector("buttonAction:"), for: .touchUpInside)
// other cell element setup
return cell
}
And at button action get the item from array using button tag i.e
func buttonAction(sender: UIButton) {
let data = tableArray[sender.tag]
// logic to pass present detailVC
}
Hope this will work!!
If you are using collection view you can use the following
// Set The Click Action On Button
cell.bProfileImage.addTarget(self, action: #selector(connected(sender:)), for:
.touchUpInside)
cell.bProfileImage.tag = indexPath.row
Then in your function
// Function For TouchUpInside For Cell
#objc func connected(sender: UIButton) {
let data = individualChatsListArray[sender.tag]
print(data.name)
}
I have a collection view and inside a button.
When I tap this button I want to change its name.
I managed to do it but when I tap it, it changes if I have 20 buttons 10 of them like this if I tap the first button it changes the 0,2,4,6,8 and if I tap a button which isn't in the list then all buttons are checked.
This is my code:
#IBAction func following(sender: AnyObject) {
if follow.tag == sender.tag {
follow.setTitle("Following", forState: .Normal)
}
print("\(follow.tag) \(sender.tag)")
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Interest Cell", forIndexPath: indexPath) as! ThirdTabCell
cell.follow.tag = indexPath.row
return cell
}
Inside button's function I tried without if statement too but again the same problem.
When I print the tags it prints only the button which was tapped.
I also tried this to my ViewController
cell.follow.addTarget(self, action: #selector(ThirdTab.follow(_:)), forControlEvents: UIControlEvents.TouchUpInside)
//inside cellForItemAtIndexPath
func follow(sender:UIButton!) {
}
Just a short overview, So you get your answer
UICollectionView is highly optimized, and thus only keep On-screen visible rows in memory. Now, All rows Cells are cached in Pool and are reused and not regenerated. Whenever, user scrolls the UICollectionView, it adds the just-hidden rows in Pool and reuses them for next to be visible rows.
So, now, coming to your answer
When you tap on button, its title will get updated, but when you will scroll your collection view, the same cell with "updated button text" will be reused and that will cause the issue you are seeing.
SOLUTION
SAVE button state in an array, in your action method
#IBAction func following(sender: AnyObject) {
if follow.tag == sender.tag {
array[sender.tag] = "<text>"
collectionView.reloadData()
}
print("\(follow.tag) \(sender.tag)")
}
and inside your datasource method
update your button text like below:
//trick is to update your button text for each index
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell{
//SAMPLE CODE
let buttonValue = array[indexPath.index]
// update button value for each index
//trick is to update your button text for each index
cell.button.setTitle("", forState: .Normal)
}
Please change below the set title line in following function.
Existing Line:
follow.setTitle("Following", forState: .Normal)
New Line:
follow.setTitle("Following", forState: UIControlState.Normal)
Hope this is working for you.
Thanks
I would suggest you to approach it this way.
Inside cellForItemAtIndexPath you can assign a buttons outlet like this
cell.followOutlet.tag = indexPath.row
and then inside your follow function you can do this
#IBAction func following(sender: AnyObject) {
let follow = sender as! UIButton
let indexP = NSIndexPath(forRow: follow.tag, inSection: 0)
let cell = yourCollectionView.cellForItemAtIndexPath(indexP) as! yourCellName
follow.setTitle("Following", forState: .Normal)
}
And now you can do whatever you want with your button.
I'm attempting to implement accessoryButtonTappedForRowWithIndexPath: in Swift 2 on a UITableViewController.
As I explain below, I think I'm missing something in when I create the disclosureIndicator, but I don't know what. It gets drawn from code, but my target action doesn't get called. UGH!
To do this programmatically, my understanding is I need to add the detailDisclosure indicator in cellForRowAtIndexPath before my cell is returned. I'm doing that as follows:
// Create disclosure indicator button in the cell
let disclosureIndicatorButton = UIButton(type: UIButtonType.DetailDisclosure)
disclosureIndicatorButton.addTarget(self, action: "disclosureIndicatorPressed:event:", forControlEvents: UIControlEvents.TouchUpInside)
customCell.accessoryType = .DisclosureIndicator
In my code, the detailDisclosure chevron gets drawn, but the target action method I assigned to it doesn't get called.
Then I need to create a handler for the button when it's pressed:
func disclosureIndicatorPressed(sender: UIButton, event: UIControlEvents) {
print("disclosure button pressed")
// convert touches to CGPoint, then determine indexPath
// if indexPath != nil, then call accessoryButtonTappedForRowWithIndexPath
}
Finally accessoryButtonTappedForRowWithIndexPath contains code to perform the segue, which I can do. What am I missing?
https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableViewDelegate_Protocol/#//apple_ref/occ/intfm/UITableViewDelegate/tableView:accessoryButtonTappedForRowWithIndexPath:
Not sure why you are adding disclosure button indicator like that in code.
What you are looking for is simply 2 step process -
Step 1 : Add correct accessoryType on cell:
cell.accessoryType = .DetailDisclosureButton
Step 2 : Take action when the button is tapped by creating the accessoryButtonTappedForRowWithIndexPath function:
override func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) {
doSomethingWithItem(indexPath.row)
}
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()
}