Using a button on a cell to pass data in swift - ios

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)
}

Related

Error in updating an array when a table view with two buttons is selected

I have a tableview with two buttons and a label and I want to save the text in the label in two different arrays based on the button I select.
| _Add Button_______Label______________Favourites Button___|
| _Add Button_______Label______________Favourites Button___|
When I select the add button I want the label to be saved in Add Button Array and when I select the Favorites Button I want to save the text in favorites.
The code I did below works fine for the Add Button but always stores the first cell for the favorite button. What am I missing here?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! SegmentControlCell
cell.addPointsLabel.text? = SegmentNetworkCallObj.AFResponse[indexPath.row]["Description"] as? String ?? ""
cell.addTaskButton.tag = indexPath.row
cell.addTaskButton.addTarget(self, action: #selector(addButtontapped(_:)), for: .touchUpInside)
cell.addTaskButton.addTarget(self, action: #selector(favouriteButtonTapped(_:)), for: .touchUpInside)
return cell
}
//Button Selection
#IBAction func addButtontapped(_ sender: Any) {
let selectedTask = SegmentNetworkCallObj.AFResponse[(sender as AnyObject).tag]["Description"]
dailyDeedsArray.append(selectedTask as! String)
print("addbutton",dailyDeedsArray)
}
#IBAction func favouriteButtonTapped(_ sender: Any) {
let selectedTask = SegmentNetworkCallObj.AFResponse[(sender as AnyObject).tag]["Description"]
favouritesArray.append(selectedTask as! String)
print("favourites",favouritesArray)
}
It seems that you have 2 buttons in each cell, one called addTaskButton and another presumably called addFavoriteButton or something similar, but you only assign & update the tag value for addTaskButton in the tableView delegate method, which is a problem for your favorites button because you access (sender as AnyObject).tag in both IBAction methods. Also, you assign 2 different targets for addTaskButton, which I guess is a mistake.
I think the code should look something like this:
cell.addTaskButton.tag = indexPath.row
cell.addTaskButton.addTarget(self, action: #selector(addButtontapped(_:)), for: .touchUpInside)
cell.addFavoriteButton.tag = indexPath.row
cell.addFavoriteButton.addTarget(self, action: #selector(favouriteButtonTapped(_:)), for: .touchUpInside)

Swift handle Play/Pause button inTableview Cell

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
}

Table View Cell Button, Change Button Image once Clicked

Currently my project has a Table View Controller with a button and text on each row. I am having a problem figuring out how to change the image of the button on a particular row once the button is clicked. I already have the function "override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)" working to take my application to a new view. I need the button (when clicked) to add the data on a particular row to a favorites variable. Which would then be displayed on a different screen. My button does not change the view.
Currently I have a button event function that is called every-time a button in a row is clicked. The problem I am having is I have no idea how to access a that particular button in the row that was clicked and only change the image of that button.
Here is my code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
// Setting Text and images
cell.batchName.text = processes[indexPath.item].batch_name
cell.startTime.text = processes[indexPath.item].starttime
cell.endTime.text = processes[indexPath.item].endtime
cell.slaTime.text = processes[indexPath.item].SLA
var statusColor = UIColor.black
// Calling Button event function
cell.starButton.addTarget(self, action: #selector(favoriteStarClicked), for: UIControlEvents.touchUpInside)
return cell
}
#IBAction func favoriteStarClicked(_ sender: Any) {
// Need to change the image of the button that was clicked
}
The modern Swift way is a callback closure
In the model add a property for the favorite state
var isFavorite = 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 Selected, then select the star image in the Image popup (leave the image for state Default empty).
In the custom cell add a callback property and an action for the button (connect it to the button). The image is set via the isSelected property of the button
var callback : (()->())?
#IBAction func push(_ sender: UIButton) {
callback?()
}
In cellForRow set the image depending on isFavorite and add the callback
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
// Setting Text and images
cell.batchName.text = processes[indexPath.item].batch_name
cell.startTime.text = processes[indexPath.item].starttime
cell.endTime.text = processes[indexPath.item].endtime
cell.slaTime.text = processes[indexPath.item].SLA
var statusColor = UIColor.black
let item = processes[indexPath.row]
cell.button.isSelected = item.isFavorite
cell.callback = {
item.isFavorite = !item.isFavorite
cell.button.isSelected = item.isFavorite
}
return cell
}
The callback updates the model and the image in the cell
No protocol / delegate.
No custom target / action.
No tags.
No index paths.
No cell frame math.

addTarget for button in tableView

I'm trying to add the download button for some items in my tableView. I've created the custom cell class and added the label and the button outlets, everything is working in displaying the info and even the buttons are showing where it should be.
I'm trying to add the target, but it does nothing. I need to pass the row index to the buttonClicked function or should I create this function in the custom cell class and then do the action some how? I would like to know the best practise of this.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PlaylistCell", for: indexPath) as! PlaylistTableViewCell
let playlist = self.playlists?[indexPath.row]
cell.titleLabel.text = playlist?.getTitle()
if (playlist?.isOfflineAvailable())! {
cell.downloadButton.isHidden = false
} else {
cell.downloadButton.isHidden = true
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
}
return cell
}
func buttonClicked(sender: UIButton) {
let buttonRow = sender.tag
print(buttonRow)
}
I've also tried removing the (sender:) from #selector, but it does not change a functionality.
In order to handle button callback in your view controller, you have two choices:
Target-action:
Add target-action in cellForRow method just as you did. Your code is probably not working because you are hiding the button when it should be visible, aren't you?
I guess you need to replace this
if (playlist?.isOfflineAvailable())! {
cell.downloadButton.isHidden = false
} else {
cell.downloadButton.isHidden = true
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
}
With this:
cell.downloadButton.isHidden = playlist?.isOfflineAvailable()
cell.downloadButton.tag = indexPath.row
cell.downloadButton.addTarget(self, action: #selector(buttonClicked(sender:)), for: .touchUpInside)
You should update tag every time because cell are reused in tableView and if don't do it every time when cellForRow is called, you may easilly get a case when a callback is called but it's tag belongs to indexPath from the previous cell usage. Also I've changed isHidden logics to the opposite. I guess you should hide the button when isOfflineAvailable returns true, right?
Delegate pattern
It is described a million of times here on SO and on many other sites as well. Basically you define a cell protocol, implement it in your controller and send callbacks from cell to it's delegate whenever a button is pressed. You can find more details in my answer for a similar question.

How to change the title of a button inside a cell Swift

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.

Resources