Swift - send event down chain after being handled by a gestureRecogniser - ios

I have a use case where I'm adding a UITapGestureRecogniser to a UITableViewCell subclass which already has existing behaviour for navigating to another screen when tapped. The issue is that the tap seems to be 'captured' by the recogniser and then not sent on afterwards - the code in my custom recogniser is called, but tapping the cell no longer navigates to a different screen. Preliminary research suggests that if a recogniser handles a gesture then it isn't sent any further along - is it possible to change this? I have researched the responder chain but to no avail.
Cheers

For the benefit of anyone else who might happen upon this same issue in the future, I have solved my problem using this answer. All I needed to do was the following:
let recogniser = UITapGestureRecognizer(target: self, action: #selector(clickHandler))
recogniser.cancelsTouchesInView = false

Related

How to block tap gesture on UIPageViewController with Scroll TransitionStyle

I want to block the tap gesture (perfectly just the left border one, but both will be also fine, I will just add button for it) which is responded for changing pages in UIPageViewController.
I already tried this solution in 'viewDidLoad' method:
for recognizer in gestureRecognizers {
if recognizer is UITapGestureRecognizer {
recognizer.isEnabled = false
}
}
But it worked only for the case when TransitionStyle is set for Page Curl, in my case I need to use Scroll TransitionStyle.
Ps. I also found a comment in UIPageViewController implementation that gestureRecognizers are populated only if transition style is UIPageViewControllerTransitionStylePageCurl so there will be needed some bigger "hack", hope you can help me with it.
Pps. Yes I found this - UIPageViewController returns no Gesture Recognizers in iOS 6 . solution but it's pretty old and in objC and I would be glad to use Swift here.
Ppps. Setting dataSource on nil won't work - I need swipe gesture.
You need to implement the UIGestureDelegate and then to declane gesture when such are detected.
I think here you can see good example how to do it

UITapGestureRecognizer doesn't tap while UIScrollView is scrolling

Basically I have a scrollView, on which there is another view.
On that view I also have a UITapGestureRecognizer.
I'm animating scrolling of the scrollView. However, during this scrolling animation, tap gesture recognizer doesn't actually recognize taps.
Any ideas?
Without code We can't give suggestion but try with below code might be useful for you!
tapGestureRecognizer.numberOfTapsRequired = 1
tapGestureRecognizer.isEnabled = true
tapGestureRecognizer.cancelsTouchesInView = false
For best practise, you can use button action instead of UITapGesture by adding UIButton. the reason is simple, to manage the button is far much simple then the UITapGesture but I am not saying use UIButton instead of tap gesture all the time, its all about your requirement.
For your case, add this line
view.userInteractionEnabled = false
and make sure your view is added in the UITapGestureand UITapGesture method is properly implemented in your code work.
For more basic, go with these two tutorials,
First tutorial |
Second tutorial
If you still face any problem then let me know.

UITapGestureRecognizer issue in iOS 9

I guys,
today I've updated my iPhone to iOS 9 and now have problems with a gesture recognizer.
This is the error:
WARNING: A Gesture recognizer (; target=
<(action=onVideoTap:, target=)>>)
was setup in a storyboard/xib to be added to more than one view
(->; layer = >) at a time, this was never allowed, and is now enforced.
Beginning with iOS 9.0 it will be put in the first view it is loaded
into.
I didn't have this problem with iOS8.
The view contains a UIImageView and a TextView. The recognizer was dropped into the ImageView and has only referncing outlets to this view.
I don't really understand this issue.
Can somebody help me? Thank you :)
This was happening with me because I wanted to use a Tap Gesture Recognize with an image in a TableViewCell contained in a TableView.
The problem is that:
I add one Tap Gesture Recognizer but I have more than one TableViewCell (more than one image) in my table.
iOS will assign the UITapGestureRecognizer to the first image in the first cell, and other cells will be without gestures (the gesture already set to the first image only).
To Solve this problem follow this:
Make sure you checked User Interaction Enabled for the UIView you want to assign with a TapRecognizerGesture.
in the sub-view TableViewCell in my case add a new UITapGestureRecognizer. The code:
internal let tapRecognizer1: UITapGestureRecognizer = UITapGestureRecognizer()`
In your main view TableView in my case and for every cell assign the UITapGestureRecognizer you made with each cell to a main function in the main view:
cell.tapRecognizer1.addTarget(self, action: "img_Click:")
cell.img.gestureRecognizers = []
cell.img.gestureRecognizers!.append(cell.tapRecognizer1)
Add the function you want UITapGestureRecognizer to fire when clicked:
func img_Click(sender: UITapGestureRecognizer) {
print("ok")
}
Notes:
You can use simple way if you do not want the UITapGestureRecognizer action in the main view by assigning it in its sub-view directly.
in step 4 function name must be the same as in addTarget line.
I think that this problem happen when you use storyboard added a Tap Gesture Recognizer. Because some reasons you added more than one views.(see the picture).
So, delete other wrong views,leave the right one view.
Already fixed it.
The storyboard is localized and in one language I assigned the the recognizer twice to the picture view.
Somehow this seemed to cause troubles on the other storyboards too.

How to get stepper and longpress to coexist?

I tried setting up a view with a longpress gesture and a stepper configured for continuous updates. With the longpress, the continuous feature of the stepper does not occur. For now, I've disabled the longpress. I guess I don't need it. But for future reference, how would I allow for both to coexist?
Just to be clear, here is the way the screen was set up when I tried this.
App was set up with a simple view controller.
A subview was added to this view (could have been a controller, but I just made it a UIView).
Several labels and stepper were added to this subview.
The steppers were wired up as outlets and actions.
A longpress recognizer was added to the main view in IB.
For completeness, a tap gesture was also added to the main view in IB.
Taps on the main view function as expected. Taps on the steppers function as expected. Longpress on the main view functions as expected. Longpress on the stepper does not.
I modified the code called by the longpress to check for the frame of the subview and not act if the touch location was within that rectangle, but that didn't make a difference. I did not try getting the longpress to fail in that situation, but I suppose I'll try that next. EDIT: OK, maybe not. There doesn't seem to be an API for that. However, there is this kludge, that I'm not going to try.
Attached is a screen shot from profiler with an inverted call tree so you can see what each item is being called by.
darkStepped: is the IBAction that is called by the stepper. If the stepper were triggered by a gesture recognizer, wouldn't I expect to see the gesture recognizer in the call tree?
If the stepper were triggered by a gesture recognizer, wouldn't I expect to see the gesture recognizer in the call tree?
The stack trace reveals that the stepper's _updateCount method is dispatched through a timer.
This could be related to the fact that a stepper has an "autoIncrement" mode where, as long as your keep it pressed, it will update at a given (varying) rate. So, instead of simply calling _updateCount, the stepper sets up a timer to handle this behaviour.
For whatever reason the timer is used, the timer explains why you do not see the gesture recogniser in the stack trace.
In your case what happens is that the stepper gets the touches, handles them, and do not forward them to any gesture recognisers attached to it.
This can be explained as follows, although this snippet does not explicitly mention a long press recogniser in relation to a UIStepper control:
According to Apple Docs:
Interacting with Other User Interface Controls
In iOS 6.0 and later, default control actions prevent overlapping gesture recognizer behavior. For example, the default action for a button is a single tap. If you have a single tap gesture recognizer attached to a button’s parent view, and the user taps the button, then the button’s action method receives the touch event instead of the gesture recognizer. This applies only to gesture recognition that overlaps the default action for a control, which includes:
A single finger single tap on a UIButton, UISwitch, UIStepper, UISegmentedControl, and UIPageControl.
...
If you have a custom subclass of one of these controls and you want to change the default action, attach a gesture recognizer directly to the control instead of to the parent view. Then, the gesture recognizer receives the touch event first. As always, be sure to read the iOS Human Interface Guidelines to ensure that your app offers an intuitive user experience, especially when overriding the default behavior of a standard control.
So, it seems you can attach the gesture recogniser directly to the control (possibly you need to subclass UIStepper for this to work, I am not really sure how to interpret the last paragraph). Hopefully this will not disable the basic workings of the stepper (but maybe it will).
After carefully reviewing Apple's docs again, I've found the solution. I added the view controller as the delegate to the longpress gesture recognizer
self.longPress.delegate = self;
(and, of course, adding <UIGestureRecognizerDelegate> to the interface, and then added this method to the view controller:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// Determine if the touch is inside the custom subview
if (gestureRecognizer == self.longPress) {
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(self.antControl.frame, touchLocation)) {
return NO;
}
}
return YES;
}
This way the gesture recognizer doesn't even get called when the longpress occurs within the frame of self.antControl, which is the subview mentioned in the question.

UITableView - move cell - How to enter dragging mode without long press?

I am using the standard UITableView editingMode to move Cells via Drag & Drop. Works like a charm, perfectly integrated to my Core Data Model and everything.
However, usability-wise I dislike that the user has to long-press the Editing Accessory (|||). I would like to change the minimum duration of the UILongPressGestureRecognizer to something like 0.1f.
The trouble: I cant seem to access the right Gesture Recognizer. UITableViewCell's gestureRecognizers-array is empty, the UITableView's gestureRecognizers array contains only private recognizers:
UIScrollViewDelayedTouchesBeganGestureRecognizer
UIScrollViewPanGestureRecognizer
UISwipeGestureRecognizer
UIGobblerGestureRecognizer
I looked at several github-Projects:
https://github.com/bvogelzang/BVReorderTableView
https://github.com/FlorianMielke/FMMoveTableView https://github.com/mystcolor/JTGestureBasedTableViewDemo https://github.com/shusta/ReorderingTableViewController
They all focus on re-engineering UITableView so you dont have to access the built in editing mode - and instead can long press any UITableViewCell anywhere WITHOUT entering editing mode.
As I simply want to change the minimumPressDuration of the built in editing mode (and am actually fine with restricting the "access point" for the drag gesture to the Accessory View) I am reluctant to use these custom implementations potentially prone to errors and trouble.
Looking forward to your help! Thank you!!
Cheers,
Chris
You may want to access the UITableViewReorderControl, as discussed in this article, and then look for any attached gesture recognizers. If you find any, you should then be able to change the minimumPressDuration property.
Swift 5
You can change the minimumPressDuration to 0. So that you can enter drag and drop mode without a long press.
let gesture = UILongPressGestureRecognizer(target: self, action: #selector(handleLongPress))
gesture.minimumPressDuration = 0

Resources