Swift: Table View Cell perform segue to two different destinations - ios

My question:
I wanna perform segue to two different destinations in a table view cell. One is directing destination "A" by clicking the cell which handled by didSelectRowAt, Another one is directing to destination "B" by clicking the button inside the cell, for example, the labelName.
For destiation "B", I have try to use (btn.addTarget), but it cannot add parameters, so the destinatin view controller do not know the which cell was being clicked.
Any suggestion?

You can Get the indexPath of your selected TableviewCell in your btnClick function by using this.
let index = self.tableview.indexPathForSelectedRow
Now by doing this you can solve this problem you mentioned.
the destinatin view controller do not know the which cell was being clicked

you can use button.tag and assign the tag of every button with your indexpath.row
button.tag = indexPath.row
button.addTarget(self, action: "btnClick:", forControlEvents: UIControlEvents.TouchUpInside)
func btnClick(sender:UIButton) {
let buttonRow = sender.tag
}
for swift 4 :
button.addTarget(self, action: #selector(händlerButtonTapped), for: .touchUpInside)
#objc func händlerButtonTapped(sender:UIButton) {
let buttonRow = sender.tag
}

Here is my cellForRowAt:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! contentTableViewCell
let content: contentModel
content = contentList[indexPath.row]
contentViewController().getUserProfilePic(userID:content.userID!)
cell.detailLabelName.text = content.name
cell.detailLabelTime.text = content.time
cell.detailTextViewContent.text = content.content
if UserDefaults.standard.data(forKey:content.userID!) != nil {
cell.detailImageSelfie.image = UIImage(data:UserDefaults.standard.data(forKey:content.userID!)!)
} else {
cell.detailImageSelfie.image = #imageLiteral(resourceName: "defaultIcon")
}
return cell
}

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)

UITableViewCell images disappear when scrolling

I have a UIButton inside a UITableViewCell where the image changes once the button is tapped. Though the selected buttons get selected as intended, once the UITableView scrolls, the selected images disappear since the cells are reused.
I'm having trouble writing the logic. Please help.
My code is below, in Swift 3.
CellForRow:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//Button_Action
addSongButtonIdentifier(cell: cell, indexPath.row)
}
This is where the cell is created:
func addSongButtonIdentifier(cell: UITableViewCell, _ index: Int) {
let addButton = cell.viewWithTag(TABLE_CELL_TAGS.addButton) as! UIButton
//accessibilityIdentifier is used to identify a particular element which takes an input parameter of a string
//assigning the indexpath button
addButton.accessibilityIdentifier = String (index)
// print("visible Index:",index)
print("Index when scrolling :",addButton.accessibilityIdentifier!)
addButton.setImage(UIImage(named: "correct"), for: UIControlState.selected)
addButton.setImage(UIImage(named: "add_btn"), for: UIControlState.normal)
addButton.isSelected = false
addButton.addTarget(self, action: #selector(AddToPlaylistViewController.tapFunction), for:.touchUpInside)
}
The tap function:
func tapFunction(sender: UIButton) {
print("IndexOfRow :",sender.accessibilityIdentifier!)
// if let seporated by a comma defines, if let inside a if let. So if the first fails it wont come to second if let
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]//toggle the state when tapped multiple times
}
sender.isSelected = !sender.isSelected //image toggle
print(" Array Data: ", self.sateOfNewSongArray)
selectedSongList.removeAll()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element{
print("true:", index)
selectedSongList.append(songDetailsArray[index])
print("selectedSongList :",selectedSongList)
}
}
}
There is logical error in func addSongButtonIdentifier(cell: UITableViewCell, _ index: Int) function at line addButton.isSelected = false
it should be addButton.isSelected = self.sateOfNewSongArray[index]
Since, cellForRowAtIndexpath method is called every time table is scrolled, it's resetting selected state of 'addButton'
You need to have array where you store which indexes are selected like selectedSongList array that you have. Then in your cellForRow method you need to use bool proparty from this array to give selected or deselected state to your button or in your addSongButtonIdentifier method selected state need to be
addButton.isSelected = selectedSongList.contains(indexPath.row)
Create a Model class for filling UITableView and take UIImage varaivals in that model, which will hold the current image for cell. On click on button action just change the UIImage variable with current image.
Best approach will be using a model class and keeping the track of each indiviual element in cell. But let me give you a quick fix.
Create a custom class of Button any where like this.
class classname: UIButton {
var imageName: String?
}
Go in your storyboard change the class from UIButton to classname
In your tableViewCellForIndexPath
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
let addButton = cell.viewWithTag(TABLE_CELL_TAGS.addButton) as! classname
if let imgName = addButton.imageName {
addButton.setImage(UIImage(named: imgName), for: UIControlState.normal)
} else {
addButton.setImage(UIImage(named: "add_btn"), for:UIControlState.normal)
}
addButton.addTarget(self, action: #selector(AddToPlaylistViewController.tapFunction), for:.touchUpInside)
return cell
}
Now let's move to your tapbutton implementation
func tapFunction(sender: classname) {
print("IndexOfRow :",sender.accessibilityIdentifier!)
// if let seporated by a comma defines, if let inside a if let. So if the first fails it wont come to second if let
if let rowIndexString = sender.accessibilityIdentifier, let rowIndex = Int(rowIndexString) {
self.sateOfNewSongArray[rowIndex] = !self.sateOfNewSongArray[rowIndex]//toggle the state when tapped multiple times
}
sender.imageName = sender.imageName == "correct" ? "add_btn" : "correct" //image toggle
sender.setImage(UIImage(named: sender.imageName), for:UIControlState.normal)
print(" Array Data: ", self.sateOfNewSongArray)
selectedSongList.removeAll()
for (index, element) in self.sateOfNewSongArray.enumerated() {
if element{
print("true:", index)
selectedSongList.append(songDetailsArray[index])
print("selectedSongList :",selectedSongList)
}
}
}

Multiple buttons in UITableViewCell in a 'top-down' UITableView

I am trying to work out how to action buttons within a UITableViewCell when the table view is 'top down' - as in, each new row is added to the top of the table view. I achieve this with the following code:
I use the following code to insert new items into my model array and then into the tableview:
let newItem = Item(text: inputTextView.text, time: Date())
items.insert(newItem, at: 0) //inserting into the front of model array
tableView.beginUpdates()
let indexPath:IndexPath = IndexPath(row:0, section:0)
tableView.insertRows(at: [indexPath], with: .fade)
tableView.endUpdates()
Within the cellForRowAt function I run the following code:
let cell = tableView.dequeueReusableCell(withIdentifier: postCellID, for: indexPath) as! NewPostCell
let item = items[indexPath.row]
cell.postTextLabel.text = text
cell.timeLabel.text = dateFormatter.string(from: time)
cell.selectionStyle = .none
return cell
Each of my tableview cells have three buttons in it.
How do I connect up these buttons so I know which button is pressed from which indexPath?
The problem is that if I use indexPath.row to tag the buttons, then the buttons in all cells gets tagged with 0, as each insert is happening at the top of the table at indexPath.row 0th position.
I thought of tagging the buttons with the current size of my model array, but that doesn't work either as when cells are re-used they could then be tagged with the length of the array at that point, which would be wrong.
There are a lot of apps that have 'last entry at the top' of the tableview sort of setup, with buttons in cells. So there must be a way to do this.
You can add UIButton to your UITableViewCell and access these UIButton via tag and add target method to these buttons as:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// create a new cell if needed or reuse an old one
let cell:UITableViewCell = self.tableVw.dequeueReusableCell(withIdentifier: "Cell") as UITableViewCell!
//Access UIButton
let button1:UIButton = cell.viewWithTag(10) as! UIButton
let button2:UIButton = cell.viewWithTag(11) as! UIButton
let button3:UIButton = cell.viewWithTag(12) as! UIButton
//Add Action Methods to UIButtons
button1.addTarget(self, action: #selector(FisrtButtonClick), for: .touchUpInside)
button2.addTarget(self, action: #selector(SecondButtonClick), for: .touchUpInside)
button3.addTarget(self, action: #selector(ThirdButtonClick), for: .touchUpInside)
return cell
}
Button Actions
// MARK: - UIButton Methods.
func FisrtButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
func SecondButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
func ThirdButtonClick(_ sender: Any) {
//Get Button cell position.
let ButtonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableVw)
let indexPath = tableVw.indexPathForRow(at: ButtonPosition)
if indexPath != nil {
print("Cell indexPath: \(indexPath?.row)")
}
}
Main idea
These are the basic steps you have to do:
Set unique tag values for each of the 3 button types: this will allow to identify which of the 3 buttons of a cell is pressed. (for instance tag value 1 for the first of your buttons, value 2 for the second kind, value 3 for the third button).
Link the 3 buttons to a #IBAction method.
You can also do this programmatically with target-actions: call aButton.addTarget(target:, action:, for:) for each button.
Then when a button will be pressed, you will use an helper function to determine the cell index of the button that was pressed.
Code
The #IBAction method should look like #IBAction func buttonTrigerred(_ sender: UIButton){...} and the code would be:
#IBAction func buttonTrigerred(_ sender: UIButton){
// -- retrieving the index path of the cell, as #NikhleshBagdiya posted
// Determine the indexPath of the cell
let buttonPosition = (sender as AnyObject).convert(CGPoint.zero, to: tableView)
let cellIndexPath = tableView.indexPathForRow(at: buttonPosition)
// --
// Determine which button is called
if sender.tag == 1 { ... } // the first kind of button has been pressed
else if sender.tag == 2 { ... } // second button kind
else if sender.tag == 3 { ... } // third button kind
}
Setting the tag value
Each of my tableview cells have three buttons in it.
I suppose you have designed your UITableViewCell in a Storyboard or Xib file. So for each of the 3 buttons of your cell: select the button, go to the attributes inspector, set the tag to the custom values indicated above.

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 detect which table cell is clicked in swift 2

I am developing app which users will choose one of the two pictures in one cell. My prototype cell looks like :
How can I detect when the user presses the vote button which cell is selected ?
My tableView Code :
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "NewTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! NewTableViewCell
//For left image
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].poll_photo1 ){
cell.leftImage.sd_setImageWithURL(url)
}
//For right image
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].poll_photo2 ){
cell.rightImage.sd_setImageWithURL(url)
}
//For user picture
if let url:NSURL? = NSURL(string: self.polls[indexPath.row].users[0].user_photo ){
cell.userPicture.sd_setImageWithURL(url)
}
// gets username and text
cell.userName.text=self.polls[indexPath.row].users[0].user_name
cell.description.text = self.polls[indexPath.row].poll_textfield
return cell
}
My web API:
I am assuming that your custom table view cell NewTableViewCell is having an outlet for your vote button.
Just tag your voteButton with indexPath.row and fetch the tag in its target function as shown below. You will get to know which cell's voteButton was tapped when you press your Vote Button
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("NewTableViewCell") as! NewTableViewCell
//Tagging with indexPath.row
cell.voteLeftButton.tag = indexPath.row
cell.voteRightButton.tag = indexPath.row
//This is the latest Swift 2.2 syntax for selector. If you are using the older version of Swift, Kindly check the selector syntax and make changes accordingly
cell.voteLeftButton.addTarget(self, action: #selector(voteLeftButtonPressed), forControlEvents: .TouchUpInside)
cell.voteRightButton.addTarget(self, action: #selector(voteRightButtonPressed), forControlEvents: .TouchUpInside)
return cell
}
func voteLeftButtonPressed(sender:UIButton){
print("Left Button Table cell clicked is \(sender.tag)")
}
func voteRightButtonPressed(sender:UIButton){
print("Right Button Table cell clicked is \(sender.tag)")
}
Add target/action to your button in cell configure method like this:
let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(YourController.tapAction(_:)))
Then implement tapAction method
func tapAction(sender : UITapGestureRecognizer)
{
if sender.state != UIGestureRecognizerState.Ended
{
return
}
let btn = sender.view as! UIButton
let pointTo : CGPoint = CGRectOffset(btn.bounds, btn.frame.size.width/2, btn.frame.size.height/2).origin;
let buttonPosition : CGPoint = btn.convertPoint(pointTo, toView: self.tableView)
let indexPath = self.tableView.indexPathForRowAtPoint(buttonPosition)
...
}

Resources