Passing Parameters to Action Functions in Swift - ios

I created tableView cells with an action and I need the action to know the indexPath of the cell that is selected. I can't figure out how to pass the indexPath as a parameter to the action or find any other way around it. Here is the code for the tableView cellForRowAt and the action to be performed:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! CustomCell
cell.foodName.text = self.menuItems[indexPath.row]
//adding gesture recognizer with action Tap to cell
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(Tap(gesture:index:IndexPath.row)))
cell.addGestureRecognizer(tapGesture)
return cell
}
func Tap(gesture: UIGestureRecognizer , index: Int){
print("Tap")
//necessary so that the page does not open twice
//adding the rating view
let ratingVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "rating") as! RatingView
ratingVC.foodName = selectedItem
self.addChildViewController(ratingVC)
ratingVC.view.frame = self.view.frame
self.view.addSubview(ratingVC.view)
ratingVC.didMove(toParentViewController: self)
}
I can't figure out how to pass the IndexPath.row as a parameter to Tap.

You don't have to do the functionality of selecting the cell, UITableViewDelegate already has this kind of behaviour.
By conforming to UITableViewDelegate and implementing tableView(_:didSelectRowAt:) method, you will be able to get the indexPath.row for the selected cell:
Tells the delegate that the specified row is now selected
So, after implementing tableView(_:didSelectRowAt:) you should get rid of tapGesture functionality, it should be similar to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "menuCell", for: indexPath) as! CustomCell
cell.foodName.text = self.menuItems[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Selected row: \(indexPath.row)")
}

Use didSelectRowAt Indexpath method.
For custom Tap use following steps.
Assign tag to your cell.
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.tapBlurButton(_:)))
cell.tag = indexPath.row
cell.addGestureRecognizer(tapGesture)
2.Manage tap event
func tapBlurButton(_ sender: UITapGestureRecognizer) {
//Get indexpath.row value or Indexpath Value
print(sender.view.tag)
}
Hope it helps. :)

First, remove the tap gesture. Second implement the didSelectRow delegate of the tableView like this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//add your rating view code here
let ratingVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "rating") as! RatingView
ratingVC.foodName = self.menuItems[indexPath.row] //get the selected item
self.addChildViewController(ratingVC)
ratingVC.view.frame = self.view.frame
self.view.addSubview(ratingVC.view)
ratingVC.didMove(toParentViewController: self)
}
Of course, you will want to add checks and guards to make sure you catch any errors.

Related

Open URL from API when Button is Pressed in Custom TableViewCell

I want to click a button in a custom xib file and it goes to a link passed in through an api using decodable.
I am able to make the entire row redirect to a link from the api by using:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let item = page!.tradeshows[indexPath.row]
if let eventURLS = item.url {
UIApplication.sharedApplication().openURL(eventURLS, options: [:], completionHandler: nil)
} else {print("link not working")
}
}
But I don't want to select the entire row, I only want to select the button from the custom .xib file.
I also tried:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) - > UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell",
for: indexPath) as!EventTableViewCell
let item = page!.tradeshows[indexPath.row]
cell.registerButton.addTarget(self, action: #selector(UpcomingEventsViewController.onClickedRegistrationButton(_: )),
for: .touchUpInside)
cell.registerButton.tag = indexPath.row
return cell
}
#objc func onClickedRegistrationButton(_ button: UIButton) {
let buttonLink = button.tag
}
but I'm not sure how to set the link from the json data so that the indexPath is correct using this second approach.
As the action is not directly related to the view controller it's more efficient to pass the URL to the cell and open the URL there. The target / action code is not needed.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventTableViewCell
let item = page!.tradeshows[indexPath.row]
cell.url = item.url
return cell
}
In the cell add a property url and an IBAction for the button
class EventTableViewCell : UITableViewCell {
var url : URL!
// other properties
#IBAction func pushButton(_ sender : UIButton) {
UIApplication.shared.open(url)
}
// other code
}
Side note:
Your first snippet is Swift 2 code. The method will never be called in Swift 3+
You need
#objc func onClickedRegistrationButton(_ button: UIButton) {
UIApplication.sharedApplication().openURL(NSURL(string:tableData[button.tag])!)
}

Access button index from TableView inside another TableView

I have a TableView inside Another TableView, and the inner tableView contains buttons. I am able fire a method when click on button, but not able to get the indexPath. The app is getting crashed when I use the below code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell") as! MyCell
cell.myButton?.addTarget(self, action:#selector(myButtonPressed(_:)), for:.touchUpInside)
return cell
}
#objc func myButtonButtonPressed(_ sender: AnyObject) {
let button = sender as? UIButton
let cell = button?.superview?.superview as? UITableViewCell
let clasObj = myCell()
let indexPath = myCell.InsidetabView.indexPath(for: cell!)
let index = (indexPath?.row)!
print(index)
}
I gave tag to the button on TableView, cellForRowAt indexPath Method and accessed it on buttonPressed method
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell") as! MyCell
cell.myButton?.addTarget(self, action:#selector(myButtonPressed(_:)), for:.touchUpInside)
cell.button.tag = indexPath.row
return cell
}
#objc func myButtonButtonPressed(_ sender: AnyObject) {
let button = sender as? UIButton
let row = button!.tag
}

How to get JSON id with button in tableview cell in swift4

I'm using tableView cell with button name of delete and I want to delete my JSON data from tableView cell I need to so That JSON ID to delete the JSON data from my Tableview. I'm using to get the JSON ID for that indexPath in didSelect row function. but the problem is I can't get JSON Id from that indexPath without pressing the row.
var selectedList: JSONList?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedList = JSONList[indexPath.row]
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Tableview.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? TableviewCell
cell?.Numbers.text = JSONList[indexPath.row].Number
cell?.Trash.addTarget(self, action: #selector(Deleting), for: .touchUpInside)
cell?.selectionStyle = .none
return cell!
}
//here is my delete function calling
func Deleting() {
let selectedListObj = selectedList
print(selectedListObj!.id)
}
First of all set a tag to the button which it will be in your case the
row of the IndexPath, add this code in cellForRowAt:
cell?.Trash.addTarget(self, action: #selector(deleting(_:)), for:
.touchUpInside)
cell?.Trash.tag = indexPath.row
You need to modify the deleting() function to be #objc since selectors
after swift 3+ are a must to add that and add the button as param into it:
#objc private func deleting(_ button:UIButton){
// here you got the object
let selectedObject = JSONList[button.tag]
}
use closure to get the tapped buttons cells indexpath.
update the table view cell with ibaction and call the closure whenever tapping on trash button.
class FakeTableCell: UITableViewCell{
var selectedCell: ((UITableViewCell) -> ())?
#IBAction func trashButtonTapped(){
selectedCell?(self)
}
}
then we can get the indexpath of the cell in cell for row for indexpath as follows
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = FakeTableCell()
cell.selectedCell = { selectedCell in
if let ip = tableView.indexPathForRow(at: selectedCell.center){
self.deleteData(with: ip.row)
}
}
return cell
}
func deleteData(with row: Int){
// get the object by JSONList[row]
let item = JSONList[row]
// perform deletion
}

Deselecting custom UITableViewCell that was selected by UILongPressGestureRecognizer

I have a custom UITableViewCell class My custom UITableViewCell
If user wants to select first cell, UILongPressGestureRecognizer called after that cell selected and when user had already chosen minimum one cell, then he could choose another cells without long pressing on it, just calling function didSelectRow. I tried do it by myself, but I couldn't. It was actually working but first user that was selected by long press, can't be deselected. I searched and found that I should cancel touches in view, but it didn't work for me. So cells that are called with didSelectRow are working fine, I can select and deselect, except one cell that was selected by UILongPressGesture.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) as? ContactCell {
if inSelectionMode {
selectUser(cell: cell, indexPath: indexPath)
} else {
if let cameraViewControl = presentingViewController as? CameraViewController {
cameraViewControl.smallView()
}
dismiss(animated: true, completion: nil)
}
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if tableView == contactTblView {
let tap = UILongPressGestureRecognizer(target: self, action: #selector(longTapSelectUser(_:)))
tap.cancelsTouchesInView = false
cell.addGestureRecognizer(tap)
}
}
func longTapSelectUser(_ gesture: UILongPressGestureRecognizer) {
if let cell = gesture.view as? ContactCell, let indexPath = self.contactTblView.indexPath(for: cell) {
inSelectionMode = true
selectUser(cell: cell, indexPath: indexPath)
}
}

IndexPath in tablew view cell returned is wrong on click of a label in a UITableView

I have a label inside a table view cell.
On click of the label I want to segue to another view controller after retrieving the correct indexPath.
I have added a gestureRecognizer to the label. On click of the label it returns the wrong indexPath , it does not return the index Path of the cell in which the label is.
How can I solve this problem . Any help will be appreciated. Thank you
Following is my code
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
cell = tableView.dequeueReusableCell(withIdentifier: "ViewControllerCell", for: indexPath) as! TableCell
cell.name.text = feeds[indexPath.row].name
nameClicked()
return cell
}
func nameClicked(){
cell.name.isUserInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(TrendViewController.handleTap(gestureRecognizer:)))
cell.name.addGestureRecognizer(gestureRecognizer)
}
func handleTap(gestureRecognizer: UIGestureRecognizer) {
var touchPoint = cell.name.convert(CGPoint.zero, to: self.tableView)
var clickedLabelIndexPath = tableView.indexPathForRow(at: touchPoint)!
nameFromView = feeds[clickedLabelIndexPath.row].name
print("IndexPath at touch",clickedLabelIndexPath)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "profile") as! ProfileViewController
vc.clickedLabelIndexPath = clickedLabelIndexPath
vc.nameFromView = feeds[clickedLabelIndexPath.row].name
self.navigationController?.pushViewController(vc, animated: true)
}
You have declare cell as instance property in your class,so you are adding gesture to the same cell's label. So create new cell using dequeueReusableCell and changed your method nameClicked by adding argument of type cell.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//create new cell
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewControllerCell", for: indexPath) as! TableCell
cell.name.text = feeds[indexPath.row].name
nameClicked(cell)
return cell
}
func nameClicked(_ cell: TableCell){
cell.name.isUserInteractionEnabled = true
let gestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(TrendViewController.handleTap(gestureRecognizer:)))
cell.name.addGestureRecognizer(gestureRecognizer)
}
Now change your handleTap method like this to get the correct indexPath.
func handleTap(_ gestureRecognizer: UIGestureRecognizer) {
let point = self.tableView.convert(CGPoint.zero, from: gestureRecognizer.view!)
if let indexPath = self. tableView.indexPathForRow(at: point) {
print("\(indexPath.section) \(indexPath.row)")
//Add your code here
}
}
Note: If your cell having only single cell then it is batter if you use didSelectRowAt method instead of adding gesture to label.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print(indexPath.row)
}
Maybe you should make condition for check if cell is touched ?
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ViewControllerCell", for: indexPath) as! TableCell
if cell.isTouched {
cell.setTouched
}
else {
cell.setNoTouched
}
return cell
}

Resources