UITapGestureRecognizer won't work for custom UIView class - ios

I have a custom UIView class which creates a checkbox. This UIView is in a custom table view cell.
I have this code in cellForRowAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TransactionsTableViewCell
cell.isUserInteractionEnabled = true
cell.addSubview(cell.test)
let gesture = UITapGestureRecognizer(target: self, action: #selector (self.recurringChange(_:)))
cell.test.isUserInteractionEnabled = true
cell.test.addGestureRecognizer(gesture)
}
I have have this function in the ViewController class
#objc func recurringChange(_ sender:
UITapGestureRecognizer) {
print("test")
}
When test view is tapped, it does not print test. I tried this with a normal UIView (not custom), and it worked exactly as expected.
If this helps, here is a link to the custom class: https://github.com/vladislav-k/VKCheckbox

I have tested the code in Xcode 10 and there is no problem.
You may check " cell.isUserInteractionEnabled = true"
and "cell.addSubview(test)" is added to cell before you add TapGesture.

Related

Making button clickable UITableView Xcode

I made a custom tableview cell with a nib file in my project with a button. How do I make it so that when the button is clicked, I can execute a simple task, like printing hi? I saw other posts that are similar, but don't completely understand them.
Thanks in Advance....
You should add target to your button like this :
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let identifier = "ShipmentDeliveryFactorTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: indexPath) as! ShipmentDeliveryFactorTableViewCell
let object = factorArray[indexPath.row]
cell.detailButton1.addTarget(self, action: #selector(showDetail(_:)), for: .touchUpInside)
cell.detailButton1.tag = indexPath.row
return cell
}
#objc func showDetail(_ sender: UIButton) {
let selectedCell = factorArray[sender.tag]
print("selectedCell.id")
}

Tableview button.tag throw lldb

i don't know what happen, i set the button.tag with the table row and when it reach row > 1, it will throw lldb. it works if the button.tag <= 1
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cells")! as UITableViewCell
let alertBtn = cell.viewWithTag(1) as! UIButton;
alertBtn.tag = indexPath.row
alertBtn.addTarget(self, action: Selector(("showAlert:")), for: UIControlEvents.touchUpInside)
return cell
}
Application crash on this line, because it fails to find a view with tag 1, the tag is updating in every cell with row value.
let alertBtn = cell.viewWithTag(1) as! UIButton
remove this line and Take #IBOutlet for alertBtn From UITableViewCell instead of refreshing with tag.
Swift 3X...
You are replacing your tag so first tag items are getting nil so replace this code ...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cells")! as UITableViewCell
let alertBtn = cell.viewWithTag(1) as! UIButton
alertBtn.addTarget(self, action: #selcetor(showAlert(sender:))), for: .touchUpInside)
return cell
}
func showAlert(sender:UIButton) {
let point = sender.convert(CGPoint.zero, to: self.tableview)
let indexpath = self.tableview.indexPathForRow(at: point)
}
Try to do custom UITableViewCell.
Declare protocol and delegate for Your new class class. Wire up a action and call delegate
protocol MyCellDelegate: class {
func buttonPressed(for cell: MyCell)
}
class MyCell:UITableViewCell {
weak var delegate: MyCellDelegate?
#IBAction func buttonPressed(sender: Any){
self.delegate?.buttonPressed(for: self)
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
.......
cell.delegate = self
........
}
Remember to add new protocol implementation to Your VC. You can add prepareForReuse method and reset delegate to nil when cell is reused.
If you want to get indexPath of cell containing tapped button you can use function similar to this matching your requirement.
func showAlert(sender: AnyObject) {
if let cell = sender.superview?.superview as? UITableViewCell{ // do check your viewchierarchy in your case
let indexPath = itemTable.indexPath(for: cell)
}
print(indexPath)// you can use this indexpath to get index of tapped button
}
Remove this line from cellForRowAtIndexPath alertBtn.tag = indexPath.row
If you can use Custom Cell for this purpose you can get indexpath of selected button as you were getting previously.
Create CustomCell and create IBOutlet for your button and labels etc. You can access subviews of your cell in cellForRowAtIndexPath and assign tag to your button. If you have any queries regarding CustomCell do let me know.

One click on image inside cell of UITableView

I have two images inside one cell of uitableview, these images shows to images from an external server and each tag of them has an id of item which this image represent, I need if I clicked on this image move user to new view controller which show details of this item, I force a problem, where user need to double click to show details instead of one click, the following my code:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = self.tableView.cellForRow(at: indexPath as IndexPath) as! prodctCell
Id1stMove = cell.image1st.tag
let tapGesture = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap))
cell.image1st.addGestureRecognizer(tapGesture)
cell.image1st.isUserInteractionEnabled = true
let cell1 = self.tableView.cellForRow(at: indexPath as IndexPath) as! prodctCell
Id2ndMove = cell1.image2nd.tag
let tapGesture1 = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap1))
cell1.image2nd.addGestureRecognizer(tapGesture1)
}
func imgTap()
{
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "testViewController") as? testViewController
let navController = UINavigationController(rootViewController: secondViewController!)
navController.setViewControllers([secondViewController!], animated:true)
self.revealViewController().setFront(navController, animated: true)
revealViewController().pushFrontViewController(navController, animated: true)
secondViewController?.movmentId = Id1stMove
updateCount(itemId: Id1stMove)
}
Yesterday itself I created sample and tried.I got the solution.But I could not post my answer immediately as I had some work.
Now I will give you my answer.I don't expect reputation for my below answer.
When you click or tap the image first time,it navigates.
You don't need to add TapGestureRecognizer for imageView in didSelectRowAt method.You need to add TapGestureRecognizer for image View in cellForRowAt method.
import UIKit
class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource {
let mobiles: [String] = ["iPhone", "Android"]
let images: [String] = ["iPhone.png", "android.png"]
#IBOutlet var tableViewImageTapping: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// number of rows in table view
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.mobiles.count
}
// create a cell for each table view row
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "cell")
if (cell == nil) {
cell = UITableViewCell(style:UITableViewCellStyle.default, reuseIdentifier:"cell")
}
cell?.textLabel?.text = self.mobiles[indexPath.row]
let strImageName = images[indexPath.row]
cell?.imageView?.image = UIImage(named: strImageName)
cell?.imageView?.tag = indexPath.row
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
tapGestureRecognizer.numberOfTapsRequired = 1
cell?.imageView?.isUserInteractionEnabled = true
cell?.imageView?.addGestureRecognizer(tapGestureRecognizer)
return cell!
}
// method to run when table view cell is selected
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("You tapped table view cell index is \(indexPath.row).")
}
// method to run when imageview is tapped
func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
let imgView = tapGestureRecognizer.view as! UIImageView
print("your taped image view tag is : \(imgView.tag)")
if (imgView.tag == 0) //Give your image View tag
{
//navigate to next view
}
else{
}
}
}
Output Screenshot
Printed results are
When you click the first image in first click
Then when you click the second image in first click
You need to execute this code in cellForRowAt indexPath instead did select.
As you said you want to use image id from the tag value I suggest below change in addition of adding code in cellForRowAt indexPath:
Change tapGesture code as below:
let tapGesture = UITapGestureRecognizer (target: self, action: #selector(imgTap(tapGesture:)))
And imgTap function:
func imgTap(tapGesture: UITapGestureRecognizer) {
let imgView = tapGesture.view as! UIImageView
let idToMove = imgView.tag
//Do further execution where you need idToMove
}
You are assigning the gesture recognizers in the "did select row at index path" method, this means that the user must select (tap) a cell for the gesture recognizers to be assigned to the images, and then the user must tap the image for those recognizers to react by calling "imgTap()", those are the two taps.
What you should do instead is assign the gesture recognizers in the "cell for row at index path" method, so when you create each cell you also create the gesture recognizers, that way when the user taps an image for the first time the tap is recognized and "imgTap()" is called.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) as! CustomCell
// Configure the cell...
let tapGesture1 = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap))
cell.image1st.addGestureRecognizer(tapGesture1)
let tapGesture2 = UITapGestureRecognizer (target: self, action: #selector(ItemsController.imgTap))
cell.image2nd.addGestureRecognizer(tapGesture2)
return cell
}
I also recommend changing the code a little so you can call "imgTap()" with a parameter (the id of what was tapped), instead of having 2 methods "imgTap()" and "imgTap1()".
Please don't use force unwrap as! prodctCell
Please use name swift naming conventions
Don't use .tag cell is usually reused it might break in some edge cases
Now back to your question, you already have a custom cell that holds the image.
you have several possibilities
Add the gesture recogniser to the imageView
you can change that to a button
Add a button over the image view (no title or image for it)
Then create an #IBAction for the button/gesture recogniser, and a delegate method that will call the main viewController where you can pass all the data you need from the cell to instantiate the second VC
Update
I would advice against adding the logic of handling the tap in the cellForRow the viewController is not meant to handle and manage his child logic.

How to call a function on toggle of switch in a custom cell - TableViewCell Swift

I have a switch button in a custom cell that is inserted into the tableView. How can I update the tableview on toggle of the switch button ?
Below is the function where I am inserting the custom tableViewCell that contains the toggleSwitch
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
  let cell = Bundle.main.loadNibNamed("AvailabilityTableViewCell", owner: self, options: nil)?.first as! AvailabilityTableViewCell
            
  // Configure the cell...
  cell.availabilityLabel.text = "Available"
switchIsOn = cell.availabilitySwitch.isOn
  return cell
            
}
UISwitch is a subclass of UIControl which allows you to set a target/action on it:
Implement the following function in your UIViewController:
func toggled(sender: UISwitch) {
// React to switch being toggled here
}
In your cellForRowAt: you can now the add the target/action to the cell's UISwitch:
cell.availabilitySwitch.addTarget(self, action: #selector(toggled:), for: .valueChanged)

How to send cell number to the function in TableViewCell

I'd like to know how to get cell number(indexPath.row) in the following tapPickView function. topView is on the UITableViewCell, and pickView is on the topView. If pickView is tapped, tapPickView is activated.
override func tableView(tableView: UITableView,
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(
"QuestionAndAnswerReuseIdentifier",
forIndexPath: indexPath) as! QuestionAndAnswerTableViewCell
cell.topView.pickView.userInteractionEnabled = true
var tap = UITapGestureRecognizer(target: self, action: "tapPickView")
cell.topView.pickView.addGestureRecognizer(tap)
return cell
}
func tapPickView() {
answerQuestionView = AnswerQuestionView()
answerQuestionView.questionID = Array[/*I wanna put cell number here*/]
self.view.addSubview(answerQuestionView)
}
First of all, you need to append : sign to your selector upon adding gesture recognizer in order for it to get the pickView as its parameter.
var tap = UITapGestureRecognizer(target: self, action: "tapPickView:")
Besides that, cells are reusable objects, so you should prevent adding same gesture again and again to the same view instance by removing previously added ones.
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("QuestionAndAnswerReuseIdentifier", forIndexPath: indexPath) as! QuestionAndAnswerTableViewCell
cell.topView.pickView.userInteractionEnabled = true
cell.topView.pickView.tag = indexPath.row
for recognizer in cell.topView.pickView.gestureRecognizers ?? [] {
cell.topView.pickView.removeGestureRecognizer(recognizer)
}
cell.topView.pickView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "tapPickView:"))
return cell
}
While populating the cell, you can set tag value of the pickView as indexPath.row so you can easily query that by cellForRowAtIndexPath(_:).
cell.topView.pickView.tag = indexPath.row
Assuming you already know the section of the cell you tap on. Let's say it is 0.
func tapPickView(recognizer: UIGestureRecognizer) {
let indexPath = NSIndexPath(forRow: recognizer.view.tag, inSection: 0)
if let cell = self.tableView.cellForRowAtIndexPath(indexPath) {
print("You tapped on \(cell)")
}
}
Hope this helps.
Assuming that this was not as simple as didSelectRowAtIndexPath, which I strongly recommend to first look into, passing the information to your method could look like this:
#IBAction func tapPickView:(sender: Anyobject) {
if let cell = sender as? UITableViewCell {
let indexPath = self.tableView.indexPathForCell(cell: cell)
println(indexPath)
}
}
Use didSelectRowAtIndexPath delegate method.

Resources