I ran into a pretty annoying issue here involving UIScreenEdgePanGestureRecognizerand UIButton interactions.
Here the context: I am developing an Ipad app. The view of my UIViewController contains a lot of UIButton's both at its very top and bottom. I realized after a while that, when being pressed at specific location, the UIButton's weren't behaving as usual. Taking the top row as an example, when the button is pressed at a location really close from the edge of the screen, the event linked to that button would take around .5 second to be triggered. I can also visually see it as the button takes time to be highlighted.
What the cause is: I did a bit of research and from what I could read from this post there is apparently a "conflict between the possibility of a gesture directed at your app and a gesture directed at the system". Basically as the user tap near the edges of the screen, the device is waiting for the user to swipe down or up (using UIScreenEdgePanGestureRecognizer, for example) and that is most likely why I get this delay with my UIButton's.
Now: All the posts regarding this issue are a little bit outdated. I tried different work-around and "not-that-convenient" solution:
i.e. Subclassing my UIButton's to allow the touch to "bypass" UIScreenEdgePanGestureRecognizer with the following method:
-(BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
BOOL pointInside = [super pointInside: point withEvent: event];
for (UIButton *view in self.subviews) {
if (CGRectContainsPoint(view.frame, point)) pointInside = YES;
return pointInside;
}
}
return NO;
}
But in vain, still doesn't fix the issue.
I am wondering if anyone came up with an easy solution to that ? Or maybe Apple did a fix ?
Any suggestions / solutions / comments would be greatly appreciated folks!
Related
I have an app with quite a complex UI, there's a big UIView called the cover with a UITableView underneath it. The tableView is configured with a tableHeaderView of the same height as the cover. As the tableView scrolls up, the cover moves up the screen (with various fancy animations) using a UIScrollViewDelegate. To allow users to scroll the tableView by swiping the cover, I've overridden the - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method to always return false.
I've now added some UIButton views to the cover. I've managed to make them respond to taps by changing the way I've overriden the pointInside method like this:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
BOOL isInside = [_directionsButton pointInside:[_directionsButton convertPoint:point fromView:self] withEvent:event];
return isInside;
}
The only problem now is that if you start a swipe gesture on the button, it's caught by the button and the tableView doesn't scroll. I want to be able to ignore swipe gestures on the button (so really let them pass to the view below).
Normally, I would just make the cover view the tableHeaderView, which seems to handle this kind of behaviour really well. However, I can't do this here, due to some unique animations done on the cover as the table scrolls.
Did you tried identifying the Gestures using Gesture Recognisers and doing action method that is to be called when the specified gesture is detected?
Please check this link. This may help you for that.
I'm getting intermittent reports from users on iOS 7 saying that the UIPanGestureRecognizer stops working on certain views every once in a while. They're supposed to be able to swipe a view to the right/left, but it just breaks and doesn't work for some unknown reason. Force quitting the app and relaunching it fixes the problem.
This problem never happened on iOS 6. And I don't have any code that disables the gesture recognizer at any time besides the gestureRecognizerShouldBegin delegate that forces the gesture to only recognize horizontal pans:
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer isMemberOfClass:[UIPanGestureRecognizer class]]) {
CGPoint translation = [gestureRecognizer translationInView:[self superview]];
if (fabsf(translation.x) > fabsf(translation.y)) {
if (translation.x > 0)
return YES;
}
}
return NO;
}
Did anything change in the UIPanGestureRecognizer (or just the plain UIGestureRecognizer) that could be causing this problem?
I think I finally solved this issue. Apparently iOS 7 handles gestures in subviews differently than it did in iOS 6 and earlier. To handle this, Apple implemented a new delegate:
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
If you return YES, that should get your gesture recognizer to work. I've implemented it and haven't had any issues so far (though admittedly this was a rare bug that I could never reliably reproduce, so it's possible that it just hasn't recurred yet).
For more information, see
https://stackoverflow.com/a/19892166/1593765.
Why would you return NO in gesture recognizer just because on gestureRecognizerShouldBegin: the movement is only vertical?
Since it's gesture made by user with his finger (and not made by a machine), there will always be some randomness in it's movement due to inaccuracy of moving finger. gestureRecognizerShouldBegin: will be called just after user touches the screen and the translation you get might be just a few pixels. Your recognizer will fail if user i.e. when putting his finger on screen moves it 2 pixels up, even if he then moves it 200 pixels to the right.
This shouldn't cause the gesture recognizer to be permanently disabled but you should look into it as well, since it might confuse users when their gestures are not recognized for seemingly no reason.
I have an xcode iOS project I'm working on where I have falling particles coming from the top of the screen down to the bottom. Based on the accelerometer, they can either fall fast, or slow.
They are all UIImageViews that are falling, and when I tap them, they should just stop moving. This works fine if they are moving slow enough and I seem to tap just a little bit under them. The problem with this is that when I tap right on top of them when they are moving fast, I can never seem to hit them.
What's the solution to this? Do I need to make a bigger UIImageView with a smaller UIImage centered in it? Or can I use the UIGestureRecognizer to look for taps in a larger radius?
Try using UITapGestureRecognizer
(or)
Try to catch the touch event using touchesBegan: method.
Try writing the touchesBegan: method as follows:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch * touch = [touches anyObject];
if (touch.view == UIImageView)
{
// execute the code you need to stop the UIImageView from moving
}
}
Hope this helps. All the best with your app :-)
Scrolling is not stopping when I touch over the contact labels. How can I add this feature for this open project.
https://www.cocoacontrols.com/controls/scroller
If I touch the background, it is working perfectly. I would like to have same thing for the contacts labels too.
Basically, it uses scrollview and there is an animation while scrolling. I can not make stop it when I touch over the labels.
Any help is welcome.
Though I am unfamiliar with the scroller project, maybe this can at least get you on the right path.
The likely reason why touching the contacts isn't stopping the scrolling is because the labels are receiving their own touch events for their own purpose, which is probably the desired behavior, since you would probably want to touch one of the contacts and have it do something. It's possible that since the touch events are being intercepted in that view for that reason, that you can not interact with the scroll view using the same event.
You may need to set the userInteractionEnabled property of the view surrounding each contact to false until the scrollview has stopped scrolling. There are several ways you could do this, but this might be enough to get you started on a good solution.
My condition may be similar with yours.
I build a scroll view in storyboard and a view is added to the scroll view.All of my UI component was placed in the content view including two textfields.Generally speaking, I would like to rewrite the - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event method, and end editing actions in this view.
However,rewrite the method in scrollview's superview has little help.But when I subclass the view and rewrite that method in this subclass Every thing is OK.
According to my condition ,subclass the view and rewrite - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event.Process the logic in view level.
Background: I need to look at every UITouch that happens in an application and see if the UITouch is NOT inside a certain UIImageView.
WildcardGestureRecognizer looked very promising and I tried it. (Code is here):
How to intercept touches events on a MKMapView or UIWebView objects?
It worked great for a sandbox/quickly-created application. However that application didn't reflect the complexity that the actual target project has. The target project has a Table View Controller and more.
After adding the WildcardGestureRecognizer to the more involved iPad application, I see that none of the other controls work after the gesture recognizer is added and one click happens.
Here's some code where I was playing with the idea. Again, the sandbox code does not yet have controls on it (such as a Table View Controller or even a UIButton) to see if they work after adding the gesture recognizer to the UIWindow.
Should I pick something other than the UIWindow to add the gesture recognizer to or am I going to run into the same problem regardless? Is there a better way?
sandbox code: https://github.com/finneycanhelp/GestureKata
You might want to try another approach: create a UIWindow subclass, use that in your XIB and override hitTest:withEvent:. It returns the view that has been "selected" to receive the touch events.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *v;
v = [super hitTest:point withEvent:event];
if (v == myImageView) {
// Do something. Maybe return nil to prevent the touch from getting
// sent to the image view.
}
return v;
}
Overriding this method can also be helpful when debugging.