Dequeue cell with button in table view cell - swift

Basically, I have a custom table view cell that lists potential employees. This cell includes a few labels and a button. What I want is for the button to remove the cell, but all I can find is this :
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == UITableViewCellEditingStyle.Delete {
numbers.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
Which only allows you to delete the cell if you swipe then choose "delete"
I'm thinking I should create a function in my tableViewController.swift file that will delete the cell at a given row, and in my custom cell class create an IBAction that will call said function.
What would the function to delete a cell at given row look like? But in this case, does the cell also know which row it is in or is that a job for the tableViewController?

In your cellForRowAtIndexPath set the cell.button.tag = indexPath.row
Add a target to the button: cell.button addTarget:...
In the button method, remove the item in your dataArray at index button.tag. Then refresh the tableView self.tableView reloadData

You can use the tag property of UIButton to store the index of the cell that you want to delete and use that tag property in the handler to locate the correct cell. In my sample code, I just made an assumption
that you only have one section that is why it is set to zero.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
...
...
cell.button.tag = indexPath.row
let tapGesture = UITapGestureRecognizer(target: self, action: Selector("handleDeleteTap:"))
cell.button.addGestureRecognizer(tapGesture)
}
func handleDeleteTap(sender: UITapGestureRecognizer)
{
if let button = sender.view as? UIButton
{
let indexPath = NSIndexPath(forRow: button.tag, inSection: 0)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}

1) You have to define an action for your button in cellForRowAtIndexPath:
...
myButton.addTarget(self, action: "buttonTappedDelete:", forControlEvents: .TouchUpInside)
...
2) You have to implement the selector:
2.1) Get the cell in which the button is located.
2.2) Get the index path of the cell.
2.3) Remove the cell from the table view and update your data.
func buttonTappedDelete(sender: UIButton) {
let cell = sender.superview!.superview as! UITableViewCell
let indexPath = self.tableView.indexPathForCell(cell)!
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
// update your data model, call `self.tableView.reloadData()`
}

Related

CollectionViewCell: Access to data of a cell through indexPath?

What I have:
I have a CollectionViewCell as .xib + .swift files.
Every cell gets their data from the database.
Inside every cell I have a like button.
What I want:
When I press the like button of a certain cell, I want to be able to read this data so I can change it and write the new data in the Database. So I want to change the like attribute of the dataset of a certain cell and save it in the DB
What I tried:
I have the indexPath of the cell but how can I read the data of the cell?
#IBAction func likeButton(_ sender: UIButton) {
var superview = self.superview as! UICollectionView
let buttonPosition:CGPoint = sender.convert(CGPoint.zero, to:superview)
let indexPath = superview.indexPathForItem(at: buttonPosition)
print(superview.cellForItem(at: indexPath!))
// Change picture
if sender.currentImage == UIImage(systemName: "heart.fill") {
sender.setImage(UIImage(systemName: "heart"), for: .normal)
} else {
sender.setImage(UIImage(systemName: "heart.fill"), for: .normal)
}
}
UICollectionViewDataSource
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MyCollectionViewCell.identifier,
for: indexPath) as! MyCollectionViewCell
cell.backgroundColor = .white
let newShoeCell = shoesArray?.randomElement()
// Fill cells with data
cell.imageView.image = UIImage(named: newShoeCell!.imgName)
cell.shoeTitle.text = newShoeCell!.title
cell.price.text = String(newShoeCell!.price)
cell.ratingNumberLabel.text = String(newShoeCell!.meanRating)
cell.floatRatingView.rating = newShoeCell!.meanRating
if newShoeCell!.liked {
cell.likeButtonOutlet.setImage(UIImage(systemName: "heart.fill"), for: .normal)
} else {
cell.likeButtonOutlet.setImage(UIImage(systemName: "heart"), for: .normal)
}
return cell
}
You need to change your thinking. It is not the data "of a cell" A cell is a view object. It displays information from your data model to the user, and collects input from the user.
You asked "...how can I read the data of the cell?" Short answer: Don't. You should be saving changes into your data model as you go, so once you have an index path, you should use it to index into your data model and get the data from there.
You need to figure out which IndexPath the tapped button belongs to, and fetch the data for that IndexPath from your data model.
If you look at my answer on this thread I show an extension to UITableView that lets you figure out which IndexPath contains a button. Almost the exact same appraoch should work for collection views. The idea is simple:
In the button action, get the coordinates of the button's frame.
Ask the owning table/collection view to convert those coordinates to
an index path
Use that index path to fetch your data.
The only difference is that for collection views, the method you use to figure out which IndexPath the button maps to is indexPathForItem(at:) instead of indexPathForRow(at:)
I suggest you add a property to your cell
class MyCell: UITableViewCell {
var tapAction: (() -> Void)?
...
#IBAction func likeButton(_ sender: UIButton) {
tapAction?()
...
}
}
and set it in view controller
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
...
cell.tapAction = { [weak self] in
// call to database or whatever here and then reload cell
tableView.reloadRows(at: [indexPath], with: .automatic)
...
}
In general, cell should never care what its indexPath is, nor should it make calls to superview

How to delete specific row in tableview cell when tapping button in swift

I want to delete specific row when i tap a button in that cell. but here rows are deleting on the index base. if i tap button in fifth row button but 0th index row is deleting.
please help me.
here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! EmployeeTableViewCell
//cell.empImage.image = UIImage(named: countryArray[indexPath.row])
cell.empName.text = namesArray[indexPath.row]
cell.cellExpendButton.tag = indexPath.row
cell.cellExpendButton.addTarget(self, action: #selector(expandcollapseButtonClicked), for: .touchUpInside)
cell.removeRowButton.addTarget(self, action: #selector(removerowButtonClicked), for: .touchUpInside)
return cell
}
#objc func removerowButtonClicked(sender : UIButton!) {
namesArray.remove(at: sender.tag)
self.tableView.reloadData()
}
I want to remove cell in which button i tapped.
Set removeRowButton.tag by indexPath.row, now you are setting cell.cellExpendButton.tag = indexPath.row, by default tag of any component is 0
cell.removeRowButton.tag = indexPath.row

Add switch in UITableView cell in Swift

How can I embed a UISwitch programmatically in a tableView cell in Swift?
I'm doing it like that
let shareLocationSwitch = UISwitch()
cell.accessoryView = shareLocationSwitch
Here is way you can embed a UISwitch on a UITableView cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "yourcellIdentifire", for: indexPath) as! YourCellClass
//here is programatically switch make to the table view
let switchView = UISwitch(frame: .zero)
switchView.setOn(false, animated: true)
switchView.tag = indexPath.row // for detect which row switch Changed
switchView.addTarget(self, action: #selector(self.switchChanged(_:)), for: .valueChanged)
cell.accessoryView = switchView
return cell
}
here is switch call beck method
func switchChanged(_ sender : UISwitch!){
print("table row switch Changed \(sender.tag)")
print("The switch is \(sender.isOn ? "ON" : "OFF")")
}
#LeoDabus Great! explanation.
Note: if your tableview may have more than one section then You should create a CustomCell subclassing UITableViewCell and configure your accessoryView inside UITableViewCell awakeFromNib method instead of table view cellForRowAt method. When dequeuing the reusable cell cast it to your CustomCell Here is sample from #LeoDabus
if you update the array with 3 elements and refreshing the table using self.tableView.reloadData() (for example after 2 second) you can see that with Xcode13 the switch values will be swapped and the example doesn't work. I have similar issue with my App when Xcode has been released to 13. try this: https://drive.google.com/file/d/1_1HuI_JFIYusNmsdyekGBWXsGznneiQ7/view?usp=sharing

button inside cellView I can't know which cell user pressed

I have custom cell in tableView
inside it I have a button , I want when user click on the button push viewController, how I can do that and how I can know which cell user use its button, because here not didSelectRowAtIndexPath
Create a custom button class
class CustomButton: UIButton {
var indexPath: NSIndexPath!
}
In your custom cell create the button of the CustomButton type add the following lines in cellForRowAtIndexPath
cell.yourCustomButton.indexPath = indexPath
Define IBAction like this
#IBAction func customButtonClicked(sender: CustomButton) {
let indexPath: NSIndexPath = sender.indexPath
// Do whatever you want with the indexPath
}
Add Tag of button in Cell and add Target for your transition Function
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier) as! CustomCell
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: #selector( self.transitonMethod ), forControlEvents: UIControlEvents.TouchUpInside)
return cell
}
Fetch indexPath from tag of sender Button and Fetch Cell for this indexPath.Push your controller on your navigation controller
func transitonMethod(sender: AnyObject){
let indexPath = NSIndexPath.init(index: sender.tag)
let cell = tableView.cellForRowAtIndexPath(indexPath)
self.navigationController?.pushViewController(yourController, animated: true)
}
In cellforRow method of tableview:
1) set tag of your button e.g. yourButton.tag = indexpath.row
2) set target method of yourbutton e.g.buttonPressed(sender:UIButton)
3) Now in that target method you will get indexpath.row by sender.tag
Declare an IBAction as follow as suggested in IOS 7 - How to get the indexPath from button placed in UITableViewCell :
#IBAction func didTapOnButton(sender: UIButton) {
let cell: UITableViewCell = sender.superview as! UITableViewCell
let indexPath: NSIndexPath = self.tableView.indexPathForCell(cell)
// Do anything with the indexPath
}
Or the other method :
#IBAction func didTapOnButton(sender: UIButton) {
let center: CGPoint = sender.center
let rootViewPoint: CGPoint = sender.superview?.convertPoint(center, toView: tableView)
let indexPath: NSIndexPath = tableView.indexPathForRowAtPoint(rootViewPoint!)
print(indexPath)
}
Then work with that indexPath to do whatever you need.

Change UITableViewCell's height after tableview is shown

I have a tableview, and each tableviewcell has a button.
I want to change current cell height when cell's button clicked.
How can I do this? Thank you!
Create an array that holds your selected cells index's
var selectedCellIndexs : NSMutableArray = []
In didSelectRowAtIndexPath indexPath: NSIndexPath) function add:
self.selectedCellIndexs.addObject(indexPath)
tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: .Fade)
Now in the tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath), all you need to do is check if the cell is part of the selected array and if it is return a different value.
if (self.selectedCellIndexs.containsObject(indexPath)) {
return selectedHeight
}
return notSelectedHeight
Remember to that if you want to deselect a cell you will need to remove the index path from your selectedCellIndexs when the cell is clicked.
NOTE: This is the basic work flow for when the user selects the cell to change the height. A bit more work is needed to get the cell from the button action.
You should store all tableviewcell's height in an NSMutableArray.
When user clicks on tableviewcell's button, update height in NSMutableArray.
After that reload your UITableView.
Hope this helps.
Globally declare a mutableArray.
var buttonPressedIndexPaths : NSMutableArray = []
in cellForRowAtIndexPath() method
cell?.button.tag = indexPath.row
cell?.button.addTarget(self, action: "onButtonAction:", forControlEvents: UIControlEvents.TouchUpInside)
copy paste these methods
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
{
if(buttonPressedIndexPaths.containsObject(indexPath))
{
return 90;//button pressed cell height
}
else
{
return 44;//normal
}
}
func onButtonAction(sender:UIButton)
{
var indexPath : NSIndexPath = NSIndexPath(forRow: sender.tag, inSection: 0)//section may differ based on ur requirement
if(buttonPressedIndexPaths.containsObject(indexPath))
{
buttonPressedIndexPaths.removeObject(indexPath)
}
else
{
buttonPressedIndexPaths.addObject(indexPath)
}
tableView.reloadData();
}