ScrollView gesture recognizer eating all touch events - ios

I have a UIScrollView to which I added a single tap gesture recognizer to show/hide some UI overlay using:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[scrollView addGestureRecognizer:singleTap];
and:
- (void)handleTap:(UITapGestureRecognizer *)sender {
// report click to UI changer
}
I added an easy table view to the bottom of the UIScrollView. Everything works right (scrolling both horizontally and vertically) but the problem is that taps are recognized only by the gesture recognizer (above), but not by the easy table view.
If I remove The line that registers the gesture listener, everything works fine, the table view notices taps on itself.
It's as if the gesture recognizer function "eats" the tap events on the table view and doesn't propagate them downward.
Any help is appreciated

This should solve your problem.
Detect touch event on UIScrollView AND on UIView's components [which is placed inside UIScrollView]
The idea is to tell the gesture recognizer to not swallow up the touch events. To do this you need to set singleTap's cancelsTouchesInView property to NO, which is YES by default.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
singleTap.cancelsTouchesInView = NO;
[scrollView addGestureRecognizer:singleTap];

Swift 3.0
let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleTap(_:)))
singleTap.cancelsTouchesInView = false
singleTap.numberOfTapsRequired = 1
scrollView.addGestureRecognizer(singleTap)
And the selector method be like.
#objc func handleTap(_ recognizer: UITapGestureRecognizer) {
// Perform operation
}

I think the reason is that User Interaction Enabled is set to false for UIImageView. You should set it to true to enable tapping in it

You can set which objects are to be included/excluded for touches.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gesture shouldReceiveTouch:(UITouch *)touch {
if (touch.view == [self view]) {
return YES;
}
return NO;
}

This worked for me in Swift 3 / Xcode 8
self.scrollView.touchesShouldCancel(in: ** the view you want the touches in **)
self.scrollView.canCancelContentTouches = false
Good luck!

Thanks #zambrey
Swift 2.2+ Version:
scrollView.delegate = self
let allowMultipleTouches = UITapGestureRecognizer(target: self, action: #selector(genderPressed))
allowMultipleTouches.numberOfTapsRequired = 1
allowMultipleTouches.cancelsTouchesInView = false
scrollView.addGestureRecognizer(allowMultipleTouches)
If your scroll view is in the Storyboard, don't forget to pin the Outlet in the view controller. In this example, scrollView is the Outlet of the UIScrollView.

TapGestures worked for me. The swipe on the other hand, I had to disable the scrolling and it worked.
swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeToNewImage(_:)))
swipeLeftGesture.direction = .left
scrollView.addGestureRecognizer(swipeLeftGesture)
swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(swipeToNewImage(_:)))
scrollView.addGestureRecognizer(swipeRightGesture)
scrollView.isScrollEnabled = false

You can capture any kind of gestures in the UIscrollView. Make sure you also handle some of the default properties as well like set cancelsTouchesInView property to false, it is true by default.
Also give some tag nos to your sub views to distinguish in selectors.
& also enable their User interaction to true.
let tap = UITapGestureRecognizer(target: self, action:
selector(didTapByUser(_:)))

My code.
I checked all proposed solutions and any work for me. I do not understand why. I do not understand the reason.
class MyClass: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
scrollView.contentSize = CGSize(width: UIScreen.main.bounds.size.width, height: 800)
scrollView.isUserInteractionEnabled = true
scrollView.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(manageGesture))
tap.cancelsTouchesInView = false
tap.numberOfTapsRequired = 1
scrollView.addGestureRecognizer(tap)
scrollView.canCancelContentTouches = false
self.view.addSubview(scrollView)
}
#objc func manageGesture(){
// Some action
}
}

Related

UILongPressGesture working on movement not on long press

I'm trying to add UILongPressGesture to the cell. It is working but only when I long press and move in any direction.
It should call the selector method on long press but it is calling when I long press and started to move. I'm also handling the state of the gesture but selector is not calling until I long press and start moving.
I have also tried with adding gesture to cell's content view and its UIlable element, but no luck.
let longPressGesture: UILongPressGestureRecognizer = {
let gesture = UILongPressGestureRecognizer()
gesture.addTarget(self, action: #selector(MyViewController.handleLongPressGetureForRow(_:)))
gesture.delaysTouchesBegan = false
gesture.cancelsTouchesInView = false
gesture.numberOfTouchesRequired = 1
gesture.minimumPressDuration = 0.2
return gesture
}()
cell.addGestureRecognizer(longPressGesture)
cell.tag = indexPath.row
Cells where adding gesture
Please help me. Thanks in advance.
Instead of handleLongPressGetureForRow(_:) change to self.handleLongPressGetureForRow(v:) with #objc before func
let longPressGesture: UILongPressGestureRecognizer = {
let gesture = UILongPressGestureRecognizer()
gesture.addTarget(self, action: #selector(self.handleLongPressGetureForRow(v:)))
gesture.delaysTouchesBegan = false
gesture.cancelsTouchesInView = false
gesture.numberOfTouchesRequired = 1
gesture.minimumPressDuration = 0.2
return gesture
}()
cell.addGestureRecognizer(longPressGesture)
cell.tag = indexPath.row
return cell
}
#objc func handleLongPressGetureForRow(v: UILongPressGestureRecognizer )
{
print("saghsaghghsgfsgsaghghsaghsaghghsaghashgsasa")
}

Add single, double and triple tap gestures to all "buttons" in custom keyboard in swift

I'm trying to create a keyboard the allows single tap, double tap, and triple tap. So I want to add a UITapGestureRecognizer() to each button in my keyboard. I know how to do this manually from the xib file (add each letter it's own gesture, which would take ages) but not quite sure how to do it in the Controller.
I wrote this for double tap in the viewDidLoad() method:
let doubleTap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: "doubleTapCharacter:")
doubleTap.numberOfTapsRequired = 2
for button in self.view.subviews{
button.addGestureRecognizer(doubleTap)
}
and created a doubleTapCharacter() method but it's still not working. I also want to be able to send information to the doubleTapCharacter method.
Any help would be much appreciated. Also, I'm very new to swift so if the instructions are complicated, I'd highly appreciate it if you can break it down a little.
create and add the gesture recognizers:
for button in view.subviews {
// create the gesture recognizer
let doubleTapRecognizer = UITapGestureRecognizer(target: self, action: "doubleTapCharacter:")
doubleTapRecognizer.numberOfTapsRequired = 2
// add gesture recognizer to button
button.addGestureRecognizer(doubleTapRecognizer)
}
then implement the target method:
func doubleTapCharacter(doubleTapRecognizer: UITapGestureRecognizer) {
let tappedButton = doubleTapRecognizer.view as! UIButton
print(tappedButton.titleForState(UIControlState.Normal))
}

swipe up/down gesture not working inside UIWebView

I have a UIWebView hosted inside UIViewController,
I manage to add the right\left gestures in the following way:
var swipeRight = UISwipeGestureRecognizer(target: self, action: "swiped:")
swipeRight.direction = UISwipeGestureRecognizerDirection.Right
self.webView.addGestureRecognizer(swipeRight)
The problem that it's not working with up/down directions:
var swipeDown = UISwipeGestureRecognizer(target: self, action: "swiped:")
swipeDown.direction = UISwipeGestureRecognizerDirection.Down
self.webView.addGestureRecognizer(swipeDown)
I think it's because of the scrolling and I also tried to add it to the scrollview but it still doesn't recognize the up/down gestures:
self.webView.scrollView.addGestureRecognizer(swipeDown)
UPDATE:
I tried Harry answer but it override the default action of swipe up/down and I can't scroll the page:
self.view.addGestureRecognizer(swipeUp)
self.view.addGestureRecognizer(swipeDown)
self.webView.scrollView.panGestureRecognizer.requireGestureRecognizerToFail(swipeUp)
self.webView.scrollView.panGestureRecognizer.requireGestureRecognizerToFail(swipeDown)
Add gesture recogniser to self.view not UIWebView instance.
self.view.addGestureRecognizer(swipeUp)
self.view.addGestureRecognizer(swipeDown)
Add these two lines after adding up/down gesture recognisers to web view.
self.webView.scrollView.panGestureRecognizer.requireGestureRecognizerToFail(swipeUp)
self.webView.scrollView.panGestureRecognizer.requireGestureRecognizerToFail(swipeDown)
In case of someone is still working on it, this solved my problem :
webView.scrollView.panGestureRecognizer.cancelsTouchesInView = NO;
upGesture.direction = UISwipeGestureRecognizerDirectionUp;
upGesture.cancelsTouchesInView = NO;
upGesture.delegate = self;
[webView.scrollView addGestureRecognizer:upGesture];
downGesture.direction = UISwipeGestureRecognizerDirectionDown;
downGesture.cancelsTouchesInView = NO;
downGesture.delegate = self;
[webView.scrollView addGestureRecognizer:downGesture];
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return [gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]];
}

Add UIGestureRecognizer to swipe left to right right to left my views [duplicate]

This question already has answers here:
Views Navigation Using Swipe Gesture
(5 answers)
Closed 9 years ago.
I have a UIStoryboard with different UIViewControllers, I would like to add another UIViewController (like a dashboard) that when the user swipe the ipad from left the dashboard will appear then when he swipe back the current view will be restored.
Is this possible? if yes any hint how to do it or any good tutorials for UIGestureRecognizer?
thank you.
UISwipeGestureRecognizer * swipeleft=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swipeleft:)];
swipeleft.direction=UISwipeGestureRecognizerDirectionLeft;
[self.view addGestureRecognizer:swipeleft];
// SwipeRight
UISwipeGestureRecognizer * swiperight=[[UISwipeGestureRecognizer alloc]initWithTarget:self action:#selector(swiperight:)];
swiperight.direction=UISwipeGestureRecognizerDirectionRight;
[self.view addGestureRecognizer:swiperight];
// Implement Gesture Methods
-(void)swipeleft:(UISwipeGestureRecognizer*)gestureRecognizer
{
//Do what you want here
}
-(void)swiperight:(UISwipeGestureRecognizer*)gestureRecognizer
{
//Do what you want here
}
Try this one.
Here is the swift version of above code.
Left Swipe
var swipeleft = UISwipeGestureRecognizer(target: self, action: Selector("swipeleft:"))
swipeleft.direction = .left
view.addGestureRecognizer(swipeleft)
Right Swipe
var swiperight = UISwipeGestureRecognizer(target: self, action: Selector("swiperight:"))
swiperight.direction = .right
view.addGestureRecognizer(swiperight)
Method implementation...
#objc func swiperight(sender: UITapGestureRecognizer? = nil) {
// Do what u want here
}
#objc func swipeleft(sender: UITapGestureRecognizer? = nil) {
// Do what u want here
}

Can you attach a UIGestureRecognizer to multiple views?

UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTapTap:)];
[self.view1 addGestureRecognizer:tapGesture];
[self.view2 addGestureRecognizer:tapGesture];
[tapGesture release];
In the above code only taps on view2 are recognized. If I comment out the third line then taps on view1 are recognized. If I'm right and you can only use a gesture recognizer once, I'm not sure if this is a bug or it just needs some more documentation.
A UIGestureRecognizer is to be used with a single view. I agree the documentation is spotty. That UIGestureRecognizer has a single view property gives it away:
view
The view the gesture recognizer is attached to. (read-only)
#property(nonatomic, readonly) UIView *view
Discussion You attach (or add) a gesture recognizer to a UIView object
using the addGestureRecognizer:
method.
I got around it by using the below.
for (UIButton *aButton in myButtons) {
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPress:)];
longPress.minimumPressDuration=1.0;
[aButton addGestureRecognizer:longPress];
[longPress release];
}
Then in my handleLongPress method I just set a UIButton equal to the view of the gesture recognizer and branch what I do based upon that button
- (void)handleLongPress:(UILongPressGestureRecognizer*)gesture {
if ( gesture.state == UIGestureRecognizerStateEnded ) {
UIButton *whichButton=(UIButton *)[gesture view];
selectedButton=(UIButton *)[gesture view];
....
}
For Swift 3 in case anyone requires this:
Based on Bhavik Rathod Answer above.
func setGestureRecognizer() -> UIPanGestureRecognizer {
var panRecognizer = UIPanGestureRecognizer()
panRecognizer = UIPanGestureRecognizer (target: self, action: #selector(pan(panGesture:)))
panRecognizer.minimumNumberOfTouches = 1
panRecognizer.maximumNumberOfTouches = 1
return panRecognizer
}
///set the recognize in multiple views
view1.addGestureRecognizer(setGestureRecognizer())
view2.addGestureRecognizer(setGestureRecognizer())
No you should not attach gesture recognizers to more than one view.
There is this explicit information in the Apple documentation:
Gesture Recognizers Are Attached to a View
Every gesture recognizer is associated with one view. By contrast, a
view can have multiple gesture recognizers, because a single view
might respond to many different gestures. For a gesture recognizer to
recognize touches that occur in a particular view, you must attach the
gesture recognizer to that view.
Event Handling Guide for iOS - Gesture Recognizers Apple Developer Library
While as others mention they might work in some cases it is clearly against the documentation and could change in any future iOS version.
What you can do is add separate gesture recognisers to the views you want to monitor and they can share a common action.
We can do something Like this, it's easy and simple
1) create function as below in your controller (this function will return GestureRecognizer)
-(UITapGestureRecognizer*)setRecognizer{
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(openProfile)];
[gestureRecognizer setNumberOfTapsRequired:1];
return gestureRecognizer;
}
2) now set this recognizer in multiple views
[self.view1 addGestureRecognizer:[self setRecognizer]];
[self.view2 addGestureRecognizer:[self setRecognizer]];
Well if someone does not want to code for adding gesture view for multiple buttons like kwalker has answered above, and want to do it via Interface Builder this may help you.
1) You can add Long Press gesture Recognizer from Object Library like you add other objects like UIButtons and UILabels.
Initially what I ended up using was I took only one
2) Set referencing outlets to UIButton and sent actions with File's Owner.
Note: If you have multiple UIButton or any other object you will need separate gesture recognizer for each of them. For more details please refer to this question of mine.Getting wrong UIButton tag on Long press gesture recognizer
if you have fixed view I suggest you doing something like this
[self.view1 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTapTap:)]];
[self.view2 addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTapTap:)]];
that way will reduce multiple different useless variable
You could create a generic extension on view to add gesture recognizers easily.
This is just an example but it could look like this
extension UIView {
func setGestureRecognizer<Gesture: UIGestureRecognizer>(of type: Gesture.Type, target: Any, actionSelector: Selector, swipeDirection: UISwipeGestureRecognizer.Direction? = nil, numOfTaps: Int = 1) {
let getRecognizer = type.init(target: target, action: actionSelector)
switch getRecognizer {
case let swipeGesture as UISwipeGestureRecognizer:
guard let direction = swipeDirection else { return }
swipeGesture.direction = direction
self.addGestureRecognizer(swipeGesture)
case let tapGesture as UITapGestureRecognizer:
tapGesture.numberOfTapsRequired = numOfTaps
self.addGestureRecognizer(tapGesture)
default:
self.addGestureRecognizer(getRecognizer)
}
}
}
To add a 2 tap recognizer on a view you would just call:
let actionSelector = #selector(actionToExecute)
view.setGestureRecognizer(of: UITapGestureRecognizer.self, target: self, actionSelector: actionSelector, numOfTaps: 2)
You could also easily add a swipe recognizer
view.setGestureRecognizer(of: UISwipeGestureRecognizer.self, target: self, actionSelector: actionSelector, swipeDirection: .down)
and so on.
Just remember that the target must be linked to the selector.
Override class by '<UIScrollViewDelegate>'
And use this method in .m class:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
This method will help you to enable multiple swipe on a single view..
What about re write (recreate) your GestureRecognize every time that you add a gesture recognizer pointing to the same func.
In below case it works. I am using IBOutletCollection
Swift 2:
#IBOutlet var topicView: [UIView]!
override func viewDidLoad() {
for view in self.topicView as [UIView] {
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "viewClicked:"))
}
}
func viewClicked(recognizer: UITapGestureRecognizer) {
print("tap")
}
I know this is an old post but I figured something similar and hopefully it's useful someone else. I simply stored my imageViews in an array and assigned it to to the same gesture recognizer in a function to set up each image view.
In my viewDidLoad():
imageViewList = [imageView, imageView2, imageView3]
setupImageViews(imageViews: imageViewList)
Function to setup image views:
func setupImageViews(imageViews: [UIImageView]) {
for imageView in imageViews {
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(imageTapped(tapGestureRecognizer:)))
imageView.isUserInteractionEnabled = true
imageView.addGestureRecognizer(tapGestureRecognizer)
//set up image to be displayed with the right aspect
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit // OR .scaleAspectFill
imageView.clipsToBounds = true
}
}
And in the action selector imageTapped(), you can have corresponding code for whichever image view tapped.
#objc func imageTapped(tapGestureRecognizer: UITapGestureRecognizer)
{
switch tapGestureRecognizer.view {
case imageView:
print("tapped Image View 1") //add your actions here
case imageView2:
print("tapped Image View 2") //add your actions here
case imageView3:
print("tapped Image View 3") //add your actions here
default:
print("Tap not detected")
}
_ = tapGestureRecognizer.view as! UIImageView
//additional code...
}
You can do it using this code my views which are imageviews in the xib.
- (void)viewDidLoad
{
firstIV.tag = 501;
secondIV.tag = 502;
thirdIV.tag = 503;
forthIV.tag = 504;
[self addTapGesturetoImageView: firstIV];
[self addTapGesturetoImageView: secondIV];
[self addTapGesturetoImageView: thirdIV];
[self addTapGesturetoImageView: forthIV];
}
-(void)addTapGesturetoImageView:(UIImageView*)iv
{
iv.userInteractionEnabled = YES;
UITapGestureRecognizer * textfielBGIVTapGasture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textfielBGIVTapped:)];
textfielBGIVTapGasture.numberOfTapsRequired = 1;
[iv addGestureRecognizer:textfielBGIVTapGasture];
}
- (void)textfielBGIVTapped:(UITapGestureRecognizer *)recognizer {
int tag = recognizer.view.tag-500;
switch (tag) {
case 1:
{
//firstIV tapped;
break;
}
case 2:
{
//secondIV tapped;
break;
}
case 3:
{
//thirdIV tapped;
break;
}
case 4:
{
//forthIV tapped;
break;
}
default: {
break;
}
}
}

Resources