Tap Gesture Recognizer is inhibiting touch events on UITableViewController - ios

I've set up TapGuestureRecognizer within my ViewDidLoad() to dismiss keyboard. My implementation as follows
class AddRegistrationTableViewController: UITableViewController, UITextFieldDelegate, SelectRoomTableViewControllerDelegate {
...
override func viewDidLoad() {
super.viewDidLoad()
emailAddressTextField.delegate = self
...
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
tableView.addGestureRecognizer(tapGestureRecognizer)
}
...
#objc func dismissKeyboard() {
emailAddressTextField.endEditing(true)
}
So far so good, this works in dismissing the text field.
But within the Table View Controller, I also have a segue linked up to a cell - which isn't registering the tap (because of the gesture recognizer) to follow through with the segue. I'll need to use 2 fingers to tap on the cell for the segue to be performed. And I've tried removing my above implementation of addGestureRecognizer in the code, and the segue performed as per expectation.
So this led me to believe that the gesture recogniser is inhibiting touch events from registering. Any workarounds or solution that I can implement?

Instead of tap to dismiss, implement scroll to dismiss by setting the table view's keyboardDismissMode to .onDrag. Have your interface work with the framework, not against it.

If you want to dismiss the keyboard why aren't you using the UITableViewDelegatemethod instead of UITapGestureRecognizer.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
emailAddressTextField.endEditing(true)
}
one more thing, the table view is getting the tap gesture and not cell, thats why the segues are not called.

A good solution for this would be implementing the UIGestureRecognizerDelegate:
class AddRegistrationTableViewController: UITableViewController, UIGestureRecognizerDelegate, UITextFieldDelegate, SelectRoomTableViewControllerDelegate {
...
override func viewDidLoad() {
super.viewDidLoad()
emailAddressTextField.delegate = self
...
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
tapGestureRecognizer.delegate = self // You set the UIGestureRecognizerDelegate here
tableView.addGestureRecognizer(tapGestureRecognizer)
}
...
#objc func dismissKeyboard() {
emailAddressTextField.endEditing(true)
}
// In this UIGestureRecognizerDelegate's method we will check if the text field is actually being edited
// and if it's not the case, we will cancel this touch for the gesture
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
return emailAddressTextField.isEditing
}

Related

UILable above the UITableVIew, UILable can not touch, why?

There is a UILable above the UITableVIew, the UILable and the UITableVIew are brothers, and I added GestureRecognizer to the UILable, but this gesture cannot be triggered, why?
Make sure you are set userInteractionEnabled is true
In your viewDidLoad
YOUR_Label.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(self.uilabelTapped(_:)))
YOUR_Label.addGestureRecognizer(tap)
In your ViewController
#objc func uilabelTapped(_ sender: UITapGestureRecognizer) {
print("UILabel Tapped")
}
Make sure your tableView and uilabel are child of same view
Hope this will help you

Pass object to a selector

I use this code to detect a long pressed element:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// some stuff
if let labelCell = model as? TTTLabelCell{
labelCell.delegate = self
labelCell.textLabel.addGestureRecognizer(longPressRec)
}
}
This is the gesture recognizer
longPressRec.addTarget(self, action:#selector(labelLongPressed))
And this is the selector
func labelLongPressed(){
print("Label Long pressed")
// labelCell.backgroundColor = UIColor.blue
}
What I want to achieve, as commented on the code, is to pass the selected object (labelCell) to the selector labelLongPressed() which will allow me to change some attributes. Do you have an idea how to do that?
change func as below :
func labelLongPressed(_ sender: UITapGestureRecognizer){
print("Label Long pressed")
let labelCell: TTTLabelCell = sender.view // track as per your view hierarchy
labelCell.backgroundColor = UIColor.blue
}
Firstly, I'd suggest adding the gesture recognizer to the cell itself rather than to the text label. This gives the user a bigger tap area, and makes it easier to reference the cell when the long press is recognized.
labelCell.addGestureRecognizer(longPressRec)
Add an argument to your gesture action, to pass the gesture recognizer. Then we can use the recogniser's view, to get a reference to the cell — since we are adding the gesture recogniser directly to the cell.
func labelLongPressed(_ recognizer: UIGestureRecognizer) {
guard recognizer.state == .began else { return }
print("Label Long press began!")
if let labelCell = recognizer.view as? TTTLabelCell {
labelCell.backgroundColor = .blue
}
}
Finally, when adding the target to the Gesture Recognizer, update the Selector to match our function signature.
longPressRec.addTarget(self, action:#selector(labelLongPressed(_:)))

How to add gesture to UITableViewCell?

I want to add a tap gesture to every cell in a UITableView that edits the content in it. The two ways to add a gesture are in code or through storyboard. I tried both and they failed.
Can I add a gesture to every cell in table with storyboard drag and drop? It seems to only add gesture to the first cell. Adding gesture in code, I wrote something like,
addGestureRecognizer(UITapGestureRecognizer(target: self,action:#selector(MyTableViewCell.tapEdit(_:))))
or
addGestureRecognizer(UITapGestureRecognizer(target: self, action:"tapEdit:"))
both work. But I'd like to let the UITableViewController handle this gesture because it does something with the datasource. How do I write my target and action?
EDIT:
addGestureRecognizer(UITapGestureRecognizer(target: MasterTableViewController.self, action:#selector(MasterTableViewController.newTapEdit(_:)))
it induce an error said, unrecognized selector sent to class 0x106e674e0...
To add gesture to UITableViewCell, you can follow the steps below:
First, add gesture recognizer to UITableView
tapGesture = UITapGestureRecognizer(target: self, action: #selector(tableViewController.tapEdit(_:)))
tableView.addGestureRecognizer(tapGesture!)
tapGesture!.delegate = self
Then, define the selector. Use recognizer.locationInView to locate the cell you tap in tableView. And you can access the data in your dataSource by tapIndexPath, which is the indexPath of the cell the user tapped.
func tapEdit(recognizer: UITapGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.Ended {
let tapLocation = recognizer.locationInView(self.tableView)
if let tapIndexPath = self.tableView.indexPathForRowAtPoint(tapLocation) {
if let tappedCell = self.tableView.cellForRowAtIndexPath(tapIndexPath) as? MyTableViewCell {
//do what you want to cell here
}
}
}
}
It is possible to add gesture directly to TableView cell and access the datasource in viewController, You need to set up a delegate:
In your custom cell:
import UIKit
class MyTableViewCell: UITableViewCell {
var delegate: myTableDelegate?
override func awakeFromNib() {
super.awakeFromNib()
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(MyTableViewCell.tapEdit(_:)))
addGestureRecognizer(tapGesture)
//tapGesture.delegate = ViewController()
}
func tapEdit(sender: UITapGestureRecognizer) {
delegate?.myTableDelegate()
}
}
protocol myTableDelegate {
func myTableDelegate()
}
In your viewController:
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate, myTableDelegate {
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 35
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? MyTableViewCell
cell?.delegate = self
return cell!
}
func myTableDelegate() {
print("tapped")
//modify your datasource here
}
}
However, this method could cause problems, see UIGestureRecognizer and UITableViewCell issue. In this case, when the swipe gesture successes, the selector get called twice for some reason. I can't say the second method is a bad one as I haven't found any direct evidence yet, but after searching through Google, it seems like the first method is the standard way.
You don't need to add gesture recognizer to achieve what you are doing.
Use the UITableViewDelegate method tableView:didSelectRowAtIndexPath: to detect which row is tapped (this is what exactly your tapGesture is going to do) and then do your desired processing.
If you don't like the gray indication when you select cell, type this in your tableView:didEndDisplayingCell:forRowAtIndexPath: just before returning the cell:
cell?.selectionStyle = .None
Adding gesture in awakeFromNib method seems much more easier and works fine.
class TestCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
let panGesture = UIPanGestureRecognizer(target: self,
action: #selector(gestureAction))
addGestureRecognizer(panGesture)
}
#objc func gestureAction() {
print("gesture action")
}
}
The easiest way to do this is to add the gesture in a custom UITableViewCell. An easier alternative to setting up a custom delegate pattern is to inform the view controller of the edits would be to use a handler in the form of a closure that the view controller can provide and which is called when user editing is finished. I'm assuming a textField is used to allow cell editing.
class CustomTableViewCell: UITableViewCell {
func activateTitleEditing() {
textField.isEnabled = true
textField.becomeFirstResponder()
}
// This will hold the handler closure which the view controller provides
var resignationHandler: (() -> Void)?
#objc private func tap(_ recognizer: UITapGestureRecognizer) {
guard recognizer.state == .ended else { return }
activateTitleEditing()
}
#IBOutlet weak var textField: UITextField! { didSet {
textField.delegate = self
let tap = UITapGestureRecognizer(target: self, action: #selector(tap(_:)))
addGestureRecognizer(tap)
textField.isEnabled = false
}}
}
extension CustomTableViewCell: UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
resignationHandler?()
}
}
And within your custom UITableViewController, pass in the handler to be able to make changes to your model. Don't forget to account for possible memory cycles in the closure.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// initialize and return table view cell
let cell = tableView.dequeueReusableCell(withIdentifier: K.documentCellIdentifier, for: indexPath)
assert(cell is CustomTableViewCell, "Document cell dequeuing error")
let customCell = cell as! DocumentTableViewCell
customCell.textField.text = documentModel.documents[indexPath.row]
customCell.resignationHandler = { [weak self, unowned customCell] in
guard let self = self else { return }
if let newTitle = customCell.textField.text {
self.cellModel.cells[indexPath.row] = newTitle
}
}
return customCell
}

Dissmissing Keyboard without Calling didSelectRowAtIndexPath

I have a ViewController which has textSearchTableView: UITableView and searchBar: UISearchBar
I added UITapGestureRecognizer to dissmis the keyboard
override func viewDidLoad() {
// ...
self.tap = UITapGestureRecognizer(target: self, action: "DissmissKeyboard")
self.tap.delegate = self
self.view.addGestureRecognizer(self.tap)
// ...
}
func DissmissKeyboard()
{
view.endEditing(true)
}
I have added this function to prevent breaking (didSelectRowAtIndexPath) function after selecting the cell
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
if touch.view.isDescendantOfView(self.textSearchTableView) {
return false
}
return true
}
But the problem is: when the keyboard is enabled and i want to dissmiss it,
if i click on the textSearchTableView, (didSelectRowAtIndexPath) will run
How can i dissmiss the keyboard if i click on the tableView without calling (didSelectRowAtIndexPath) ? and I don't want to break this function as well
I hope that I describe the problem well
Thanks a lot
Re-write your didSelectRowAtIndexPath like this:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if (self.searchBar.isFirstResponder())
{
self.searchBar.resignFirstResponder()
}
else
{
//Do something here
}
}
Alternatively: We usually use this approach in tableviews.
self.tableView.keyboardDismissMode = .OnDrag
This will dismiss keyboard when a drag begins in the tableview.
How about you have your tableview's delegate return nil for
- (NSIndexPath *)tableView:(UITableView *)tableView
willSelectRowAtIndexPath:(NSIndexPath *)indexPath
As willSelectRowAtIndexPath is probably what you want but not what you asked for: dismissing the keyboard is done by sending the first responder resignFirstResponder, for sake of answering the actual question.

UISwipeGestureRecognizer Custom Action with UITableView

I'm trying to add a swipe gesture (left/right) in order to hide/show my side menu.
I got it working perfectly on a UIView, however, I'm having trouble with an UITableView.
Here's my code to add my swipe gestures to my TableView:
// Add right swipe gesture recognizer
let rightSwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "toggleSideMenu")
rightSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Right
self.timelineTableView.addGestureRecognizer(rightSwipeGestureRecognizer)
// Add left swipe gesture recognizer
let leftSwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: "toggleSideMenu")
leftSwipeGestureRecognizer.direction = UISwipeGestureRecognizerDirection.Left
//sideMenuContainerView.addGestureRecognizer(rightSwipeGestureRecognizer)
self.timelineTableView.addGestureRecognizer(leftSwipeGestureRecognizer)
Here's my selector method :
func toggleSideMenu() {
println("ENTER SWIPE")
toggleSideMenuView()
}
I've also done this :
func tableView(tableView: UITableView, editingStyleForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCellEditingStyle {
return UITableViewCellEditingStyle.None
}
func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return false
}
However, my selector menu "toggleSideMenu()" is never called when I swipe left or right.
P.S: I've also tried to add those swipe gesture on my UITableViewCell directly but it doesn't work as well.
Anyone has an idea? Thanks a lot for your time!
Thanks to Kirit Modi. Here's the solution to my problem:
Add :
leftSwipeGestureRecognizer.delegate = self
rightSwipeGestureRecognizer.delegate = self
Then add the UIGestureRecognizerDelegate delegate method :
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldReceiveTouch touch: UITouch) -> Bool {
return true
}
For me helped
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
because i had two gestures. One from slide menu and one from current VC. So that one from slide menu killed VC's gestures.

Resources