UITableView as UIGestureRecognizerDelegate - swift

I would like to extend UITableView by adding a custom UIPanGestureRecognizer:
extension UITableView {
func addCustomPanGestureRecognizer() {
let panGestureRecognizer = UIPanGestureRecognizer()
panGestureRecognizer.delegate = self
addGestureRecognizer(panGestureRecognizer)
// some additional setup
// ...
}
}
Since custom gesture recognizer interferes with scrolling, I tried to implement UIGestureRecognizerDelegate with an optional requirement:
extension UITableView: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer == self.panGestureRecognizer
}
}
...but it's not getting called at all, despite delegating to tableView (as shown above).
Also, Xcode displays a warning:
Instance method 'gestureRecognizer(:shouldRecognizeSimultaneouslyWith:)' nearly matches optional requirement 'gestureRecognizer(:shouldRecognizeSimultaneouslyWith:)' of protocol 'UIGestureRecognizerDelegate'
To get rid of the warning, I tried adding #objc annotation before method definition, but with no luck.

Turns out, Xcode needed some additional help with finding an appropriate Objective-C method definition. To achieve this I had to provide method signature after #objc annotation:
extension UITableView: UIGestureRecognizerDelegate {
#objc(gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:)
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return otherGestureRecognizer == self.panGestureRecognizer
}
}
The warning is still displayed, but the method is being called properly.

Related

Detect userEvent/pan/scroll on orthogonal sections in UICollectionViewCompositionalLayout

I am using autoScroll on an orthogonal section of the collectionView using compositional layout. I need to invalidate the autoscroll timer as soon as the user manually scrolls the section.
I could use scrollViewDidBeginDragging / scrollViewWillBeginDecelerating, but the scrollView delegates never get called on orthogonal sections.
If anyone has any workaround to detect user scroll event in this case, it will be helpful. Thank you.
After trying out several solutions, I found the best and the simplest solution. I added a UIPanGestureRecogniser to the UICollectionViewCell to listen to user pan events. In the selector, I just invalidate the timer. That's it!
Also we need to return true by overriding gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) so that the vertical scroll and horizontal scrolls works properly.
This is what I added to the UICollectionViewCell class:
class CustomCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: .zero)
pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
pan.delegate = self
self.addGestureRecognizer(pan)
}
#objc private func handlePan(_ pan: UIPanGestureRecognizer) {
delegate?.invalidateTimer()
}
}
extension CustomCell: UIGestureRecognizerDelegate {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
With this, every time the user tries to scroll, I invalidate the autoScroll timer

Screen edge gesture is not recognized in PDFView (UIViewer) [Swift, iOS 15, PDFKit]

I am displaying a PDF file and would like to add a screen edge gesture to move pages around.
The following code works fine when entire the content of a PDF is displayed on the screen. However, when the PDF was zoomed, the screen edge gesture cannot even activate.
override func viewDidLoad(){
super.viewDidLoad()
//set up gesture to swipe from the edge
let leftScreenEdgeRecognizer = UIScreenEdgePanGestureRecognizer (
target: self, action: #selector(TextDocumentViewController.leftEdgePanGestureHandler(_ : )))
leftScreenEdgeRecognizer.edges = UIRectEdge.left
let rightScreenEdgeRecognizer = UIScreenEdgePanGestureRecognizer (
target: self, action: #selector(TextDocumentViewController.rightEdgePanGestureHandler(_ : )))
rightScreenEdgeRecognizer.edges = UIRectEdge.right
//register the gesture
pdfView.addGestureRecognizer(leftScreenEdgeRecognizer)
pdfView.addGestureRecognizer(rightScreenEdgeRecognizer)
}
//gesture functions here
#objc func leftEdgePanGestureHandler(_ sender: UIScreenEdgePanGestureRecognizer){
if(sender.state == UIGestureRecognizer.State.ended){
print ("Left Edge")
pdfView.goToPreviousPage(sender)
}
}
#objc func rightEdgePanGestureHandler(_ sender: UIScreenEdgePanGestureRecognizer){
if(sender.state == UIGestureRecognizer.State.ended){
print ("right Edge")
pdfView.goToNextPage(sender)
}
}
I tired to add a code like,
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
However, this is not working at all.
I was able to solve this problem thanks to the answer provided for my old question. I have totally forgotten about the post. After learning more about multiple gesture detections through try and error, I realized that I can do as follows to solve this posted question:
Enables the multiple gesture activation:
class ViewController: UIViewController, UIGestureRecognizerDelegate, UIDocumentPickerDelegate, PDFViewDelegate {
// ... other things
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
shouldRecognizeSimultaneouslyWith otherGestureRecognizer:
UIGestureRecognizer) -> Bool {
return true
}
}
Make sure to appropriately set delegate.
override func viewDidLoad(){
super.viewDidLoad()
//set gesture
leftScreenEdgeRecognizer.delegate = self
rightScreenEdgeRecognizer.delegate = self
}

Simultaneous gesture recognition for specific gestures

I'm trying to enable simultaneous gesture recognition but only for the UIPinchGestureRecognizer and UIRotationGestureRecognizer gestures. I don't want it to work for any other gestures. If I set the following property to true it allows all gestures to be recognized simultaneously, how can I limit it to just rotating and scaling?
func gestureRecognizer(UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Make sure your class implements UIGestureRecognizerDelegate
class YourViewController: UIViewController, UIGestureRecognizerDelegate ...
Set the gesture's delegate to self
yourGesture.delegate = self
Add delegate function to your class
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
if (gestureRecognizer is UIPanGestureRecognizer || gestureRecognizer is UIRotationGestureRecognizer) {
return true
} else {
return false
}
}
any 2 cents for swift 5.1
// suppose You need to prefer pinch to pan:
//UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith shouldRecognizeSimultaneouslyWithGestureRecognizer: UIGestureRecognizer) -> Bool {
if gestureRecognizer is UIPinchGestureRecognizer {
return true
}
return false
}

overload gestureRecognizerShouldBegin with UIPanGestureRecognizer in swift

I have a pan gesture and I need to check when the gesture should begin. In objective-C you can use
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
and it possible to overload with UIPanGestureRecognizer
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
but in swift
override func gestureRecognizerShouldBegin(gestureRecognizer: UIPanGestureRecognizer) -> Bool {
it doesn't work and return
Overriding method with selector 'gestureRecognizerShouldBegin:' has incompatible type '(UIPanGestureRecognizer) -> Bool'
How can I overload this method in swift?
You don't need to overload the method, you can do this instead :
override func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
if let panGesture = gestureRecognizer as? UIPanGestureRecognizer {
return ...
}
else {
return ...
}
}
Remove override, and don't forget to add the UIGestureRecognizerDelegate extension.
You can add it at the bottom of your class
class YourViewController: UIViewController {
// You class declaration
}
extension YourViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
//
return true
}
}

UITapGestureRecognizer for UIPIckerView

UITapGestureRecognizer with UIPickerView not working. Why?
class someVC: UIViewController, UITapGestureRecognizerDelegate
override func viewDidLoad() {
let tap = UITapGestureRecognizer(target: self, action: "dismissKeyboard")
self.pickerView.addGestureRecognizer(tap)
tap.delegate = self
}
func dismissKeyboard() {
textField.resignFirstResponder()
}
I solved problem by adding override getureRecognizer
func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
Looks ok, could it be that there is another gesture recognizer swallowing the tap? You need to make sure your object is on the top of the view stack to ensure it gets fired. Also you set the delegate, does the delegate methods get called? or do they not fire either? If they don't then it means your tap gesture is not being touched(another gesture is taking the touch), if it does get fired, it means there is something wrong with the action(but i doubt that is the case).