I've an UISplitViewController-Subclass with two UITableViewControllers (master & detail) in my Storyboard.
My BOSplitViewController is extended by a subview to draw in it.
_linienView = [[BODrawLinienView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:_linienView];
In BODrawLinienView i've implemented my UIPanGestureRecognizer to Draw UIBezierPaths.
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(recognizedGesture:)];
_panRecognizer.minimumNumberOfTouches = 1;
_panRecognizer.maximumNumberOfTouches = 1;
_panRecognizer.delegate = self;
_panRecognizer.cancelsTouchesInView = NO;
[self addGestureRecognizer:_panRecognizer];
And the UIGesturerecognizerDelegate methods are implemented there, too.
#pragma mark - <UIGestureRecognizerDelegate>
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint velocity = [panGestureRecognizer velocityInView:self];
return fabs(velocity.y) < fabs(velocity.x); // < >
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
The check for velocity in gestureRecognizerShouldBegin: allows me to avoid vertical events handled by the recognizer, who only should accept more or less horizontal events.
But all events avoided by this check aren't forwarded to the UITableViews in the SplitViewControlles ViewControllers.
EDIT: I've prepared a demo-project of this behavior on my Dropbox directory
Does anybody know how to realize forwarding of events to these Views?
Related
Please help me with the below scenario.
Self.view (Swipe Gesture added here)
UicollectionView Object in subView (Default pan,swipe gestures are there)
Want to disable scrollview scrolling/failCollectionViewPan gesture for 2 finger swipe event
Means collection view will not scroll if 2 finger swipe executed
Another approach can be to disable Collection view scrolling while 2 fingers used. Over here, I want not to swipe collection view on 2 finger swipe.
I have implemented this code, but its slowing down scrolling.
[self.collectionView.panGestureRecognizer shouldBeRequiredToFailByGestureRecognizer:_swipeL];
Then I have implemented below codes
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
return YES;
}
Now both, Swipe and Scroll are working together.
Then, I have tried in a below manner, but still not got fixed.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
if([gestureRecognizer isKindOfClass:[UISwipeGestureRecognizer class]]){
if(gestureRecognizer.numberOfTouches==2){
if( [[otherGestureRecognizer.view class] isSubclassOfClass:[UITableViewCell class]] ||
[NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UITableViewCellScrollView"] ||
[NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UITableViewWrapperView"] || [NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UIScrollViewPanGestureRecognizer"] || [NSStringFromClass([otherGestureRecognizer.view class]) isEqualToString:#"UIScrollViewPagingSwipeGestureRecognizer"])
{
NSLog(#"Allow&Disable %#", [otherGestureRecognizer description]);
[gestureRecognizer requireGestureRecognizerToFail:otherGestureRecognizer];
return NO;
}
}
}
return YES;
}
Also I have implemented below 2 methods to fix it but fails.
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7_0);
Its is able to fix for ScrollView with such ways, but the same ways are not working for UICollectionView. Because of, colection view's default pan gesture, it cannot be able to modified. While tring this way, app crashed.
If you want to detect pan gesture, try something like this. But you need to recognise direction of pan gesture:
- (void)viewDidLoad
{
[super viewDidLoad];
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGesture:)];
panGesture.delegate = self;
[self.yourCollectionView addGestureRecognizer:panGesture];
}
- (void)panGesture:(UIPanGestureRecognizer *)gesture;
{
if (gesture.numberOfTouches == 2) {
NSLog(#"Two fingers pan gesture");
}
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
&& gestureRecognizer.numberOfTouches == 2) {
return NO;
}
return YES;
}
Your problem with UISwipeGestureRecognizer is, that it fires later than UIPanGesture, so in the shouldRecognizeSimultaneouslyWithGestureRecognizer delegate call, swipe gesture is always otherGestureRecognizer, and UIPanGesture is always gestureRecognizer, and in this function you can only disable otherGestureRecoginzer...
UPDATE:
Another solution: use another UIPanGestureRecognizer to disable scroll pan gesture:
- (void)viewDidLoad
{
[super viewDidLoad];
UISwipeGestureRecognizer *swipeGesture = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(swipeGesture:)];
swipeGesture.direction = UISwipeGestureRecognizerDirectionDown;
swipeGesture.delegate = self;
swipeGesture.numberOfTouchesRequired = 2;
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:nil];
panGesture.delegate = self;
[self.tableView addGestureRecognizer:panGesture];
[self.tableView addGestureRecognizer:swipeGesture];
}
- (void)swipeGesture:(UIPanGestureRecognizer *)gesture;
{
NSLog(#"Two fingers swipe gesture");
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
&& gestureRecognizer.numberOfTouches == 2) {
return NO;
}
return YES;
}
This answer might be late, but I did have a similar problem today and couldn't find a solution, until I figured something out myself. It's quite simple, actually.
Swift 5
We create a gesture
let gesture = UIPanGestureRecognizer(target: self, action: #selector(functionCall))
gesture.minimumNumberOfTouches = 2
gesture.maximumNumberOfTouches = 2
We then assign that gesture to the collectionView, effectively overwriting the scroll gesture on it.
collectionView.addGestureRecognizer(gesture) // simply overwrites the 2 finger gesture on the collectionView
It now is possible to scroll the collectionView with 1, 3 or more fingers, but the 2 finger pan gesture is blocked.
I have a ParentViewController whose view has a subview. I want ParentViewController to have the initial opportunity to evaluate gestures in its view, and only upon failure of those gestures should the subview then have the chance to evaluate the gesture. But instead things seem to be going the opposite direction, and the subview is actually evaluating a pinch gesture that belongs to the parent view. Here's how things are set up:
ParentViewController has a pinch gesture recognizer whose delegate is set to ParentViewController:
pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinch:)];
pinchRecognizer.delegate = self;
[self.view addGestureRecognizer:pinchRecognizer];
ParentViewController implements the UIGestureRecognizerDelegate protocol, specifically implementing the method gestureRecognizerShouldBegin:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer //DEPENDENCY
{
return [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]];
}
ParentViewController's view has a subview. The subview has a tap gesture recognizer and a pan recognizer:
_tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(stepTapped:)];
[self addGestureRecognizer:_tapRecognizer];
_tapRecognizer.numberOfTapsRequired = 1;
_tapRecognizer.delegate = self;
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
[self addGestureRecognizer:_panRecognizer];
_panRecognizer.maximumNumberOfTouches = 1;
_panRecognizer.delegate = self;
The subview also implements the UIGestureRecognizerDelegate protocol, specifically implementing the method gestureRecognizerShouldBegin:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:[self superview]];
return (fabsf(translation.x) > fabsf(translation.y) && translation.x < 0);
} else if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return YES;
}
return NO;
}
Finally, ParentViewController tells the subview's gesture recognizers to only run when its' own pinch gesture fails:
[subview.panRecognizer requireGestureRecognizerToFail:pinchRecognizer]; //GESTURE DEPENDENCY
[subview.tapRecognizer requireGestureRecognizerToFail:pinchRecognizer]; //GESTURE DEPENDENCY
Despite these last two lines of code, I find that the subview's gestureRecognizerShouldBegin: method gets called before that of ParentViewController. Furthermore, the implementation of gestureRecognizerShouldBegin: in ParentViewController won't even run unless I modify the subview's implementation to return YES if a pinch gesture is taking place (see final line):
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:[self superview]];
return (fabsf(translation.x) > fabsf(translation.y) && translation.x < 0);
} else if ([gestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
return YES;
}
return [gestureRecognizer isKindOfClass:[UIPinchGestureRecognizer class]];
}
With the above code, the subview's gestureRecognizerShouldBegin: runs and, upon returning YES at the end if it's a pinch gesture, ParentViewController's gestureRecognizerShouldBegin: gets called.
In case it isn't clear, the behavior I expect is that the ParentViewController's gestureRecognizerShouldBegin: always gets called first, and only upon failure will the subviews' implementation get called.
I'm working on a custom swipe event for a UITableView that uses custom UITableViewCell subclass. I included the UIGestureRecognizerDelegate in my header, and have this in viewDidLoad:
UISwipeGestureRecognizer *swipeLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(didSwipe:)];
swipeLeft.direction = UISwipeGestureRecognizerDirectionLeft;
swipeLeft.numberOfTouchesRequired = 1;
[self.tableView addGestureRecognizer:swipeLeft];
My swipeLeft method looks like so:
-(void)didSwipe:(UISwipeGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded)
{
CGPoint swipeLocation = [recognizer locationInView:self.tableView];
NSIndexPath *swipedIndexPath = [self.tableView indexPathForRowAtPoint:swipeLocation];
NSDictionary *clip = [self.clips objectAtIndex:swipedIndexPath.row];
NSLog(#"Swiped!");
}
}
It's sort of working, but the swipe has to be incredibly precise. Like nearly impossibly precise.
I almost got it working by using a UIPanGestureRecognizer instead, but unfortunately it didn't play nice with the global side drawer component that uses a global pan gesture recognizer (ECSlidingViewController for those interested).
Is there any way around this? Any help would be appreciated, as I've been googling around and browsing SO for hours looking for a solution.
As pointed out by Kolin Krewinkel on Twitter, implementing these 2 delegate methods did the trick:
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
return YES;
}
I was having a similar problem using the ECSlidingViewController and swipe-to-delete on a UITableView (the top view controller in my case slides to the left to reveal the menu).
I fixed the problem by adding a delegate to the default panGesture property of my ECSlidingViewController like this to only pull in the menu if the swipe starts in the very right-hand edge of the screen:
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer locationInView:gestureRecognizer.view].x > ([UIScreen mainScreen].bounds.size.width - 60.0))
{
return YES;
}
return NO;
}
What I have is my view controllers view with several subviews. The hierarchy looks something like:
[view controller view]
[container view]
[view1,view2,view3,view4,view5,etc...]
[gesture view (full screen)]
I'm trying to implement pinch and 2-finger panning, but cannot get them to work simultaneously:
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePinch:)];
pinch.delegate = self;
UIPanGestureRecognizer *pullDownContainerView = [[UIPanGestureRecognizer alloc]initWithTarget:self action:#selector(pullingDownContainerView:)];
pullDownContainerView.minimumNumberOfTouches = 2;
pullDownContainerView.maximumNumberOfTouches = 2;
pullDownContainerView.delegate = self;
[self.touchView addGestureRecognizer:pullingDownContainerView];
[self.touchView addGestureRecognizer:pinch];
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
return YES;
}
Another strange thing is that shouldRecognizeSimultaneouslyWithGestureRecognizer isn't being called (I have implemented the UIGestureRecognizerDelegate protocol).
They do work individually. I've read something about using a scroll view where pan and pinch are readily available or something. So I'd need to replace the container view's UIView with a UIScrollView
Would like some guidance.
Update:
Did a quick clean and build and now it's working!
I am using UIPageController to implement a page based navigation application. I would like to enable the swipe gestures only at the margin of the page and prevent that from the inner content view. The page has a margin of 20 pixels:
contentView.frame = CGRectInset(self.view.frame, 20, 20);
Upon recognising a gesture you should be able to retrieve the locationInView and then if this is an acceptable value proceed, otherwise not.
First add in the class interface.
#interface MyPageViewController :UIViewController<UIPageViewControllerDelegate, UIGestureRecognizerDelegate>
Then in viewDidLoad add
for (UIGestureRecognizer *recognizer in self.pageViewController.gestureRecognizers) {
recognizer.delegate = self;
}
Then implement shouldReceiveTouch method
#pragma mark - UIGestureRecognizer delegate methods
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
CGPoint touchPoint = [touch locationInView:self.pageViewController.view];
CGRect innerRect = CGRectInset(self.pageViewController.view.frame, 40, 40);
if (CGRectContainsPoint(innerRect, touchPoint)) {
return NO;
}
return YES;
}