Detect multitouch in a custom UIViewController contains a UITableView subview - ios

I have a UITableView as a subview in the UIViewController's view. I understand that I can detect the touch event by overwritting
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
}
But it never detected these events in the ViewController if I touched the tableview. As suggested in this forum by others, I should had a custom UITableView and added the above lines in it. I did that, but still the viewController did not detect the touch event. Any suggestions? Thanks.

After a few days of Googling, I finally found the solution that works for me. The key is to pass the responder chain to the next responder. For instance, in the touch began case, add the following line.
[self.nextResponder touchesBegan:touches withEvent:event]

Related

a UITableView is embeded in a UIScrollView, swipe to delete is not work

I try a lot methods but not get solution
In my case, my view hierarchy:
UIView
UIScrollView
LeftContainer + RightContainer
(in RihgtContainer) ViewContainer
TableView
I tried these:
subclassing Scrollview and
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if(!self.dragging)
{
[self.nextResponder touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesEnded:touches withEvent:event];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
innerTableView.scrollEnabled = YES;
innerTableView.alwaysBounceVertical = NO;
You can tray to set the delaysTouchesBegan to true on your scrollview... like here https://stackoverflow.com/a/31040918/1702413
but ..
UITableview is inherited from UIScrollView
You should not embed UIWebView or UITableView objects in UIScrollView objects https://stackoverflow.com/a/17121582/1702413

why UICollectionView not calling didSelectItemAtIndexPath any more after scrolling the scrollView of it's cells?

I am using the UICollectionView to display some products. In the custom UICollectionViewCell, there is a custom UIScrollView, where there are some images which allow the users to do a quick preview.
To forward tap gesture from the UIScrollView to the UICollectionViewCell, i override the touches related methods as below:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging) {
NSLog(#"Began ==>");
[self.nextResponder touchesBegan:touches withEvent:event];
}
else {
[super touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging) {
NSLog(#"Moved ==>");
[self.nextResponder touchesMoved:touches withEvent:event];
}
else {
[super touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging) {
NSLog(#"Ended ==>");
[self.nextResponder touchesEnded:touches withEvent:event];
}
else {
[super touchesEnded:touches withEvent:event];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging) {
NSLog(#"Cancelled ==>");
[self.nextResponder touchesCancelled:touches withEvent:event];
}
else {
[super touchesCancelled:touches withEvent:event];
}
}
sometimes it doesn't work in some cases. For example, when the UICollectionView is loaded first time, didSelectItemAtIndexPath method can be called when you tap, but after some scrolls or taps, the method is not called anymore, even if you try to reload the UICollectionView, it's still not working.
I try to log the message in the touch methods of UICollectionViewCell, the touches & event is forwarded. But why the didSelectItemAtIndexPath of UICollectionView is called if the cell get the gesture?
Any advice would be much appreciated.
UPDATE:
1.the UICollectionViewCell is loaded from nib file.
It's not a good idea to use nextResponder if you're targeting a specific object. As you've just witnessed, the responder chain can change and you may not always get the object you expect. Why this is happening here can be any number of reasons, but you should use a more reliable means in this situation anyways.
Since UIScrollView is a subview of the UICollectionViewCell you can just pass the event directly to your superview [self.superview touchesMoved:touched withEvent:event]; You need to keep view hierarchy in mind when you're doing this and may have to call the superview's superview to reach the cell.
Also I wouldn't recommend using touches to intercept user interactions. I would highly recommend using UIGestureRecognizers as that will provide better more consistent functionality.
You should always call the super methods.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
NSLog(#"Began ==>");
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
NSLog(#"Moved ==>");
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
NSLog(#"Ended ==>");
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesCancelled:touches withEvent:event];
NSLog(#"Cancelled ==>");
}
Actually it's because of self.dragging understanding.
The correct way to forward the tap gesture is as below:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.dragging) {
[self.nextResponder touchesEnded:touches withEvent:event];
}
else {
[super touchesEnded:touches withEvent:event];
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesCancelled:touches withEvent:event];
}

How to know which view the user touches on screen

If there is a uiscrollview and and multiple sub view on the uiscrollview. how to know where the user touches i.e on specific view or scrollview(blank space)
Use this approach:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSLog(#"View touched: %#", touch.view);
}
This method is called every time your finger touches the screen, and the touch knows which view it touched.
Edit: It won't work on a UIScrollView because the scroll view gets the touch itself, check this:
touchesBegan method not called when scrolling in UIScrollView
How to enable touch began in UIScrollView?
You have 2 possibilities for detect the touch with - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; in a scrollView:
first, implement this method in your viewController, and add the scrollview on this viewController.
second, which I recommend: make a custom class which is inherited from UIScrollView like this:
.h:
#interface CustomScrollView : UIScrollView
#end
.m:
#import "CustomScrollView.h"
#implementation CustomScrollView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[self.nextResponder touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if(!self.dragging){
[self.nextResponder touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[self.nextResponder touchesEnded:touches withEvent:event];
}
#end
in your vc make:
...
CustomScrollView* customScrollView = [[CustomScrollView alloc]initWithFrame:CGRectMake(0, 0, 320, 480)];
...
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
NSLog(#"View touched: %#", touch.view);
}

Passing touch info to UIScrollView under another UIView

I have a UIScrollView under a transparant UIView. I need to transfer all pans and taps to the scroll view (which only scrolls horizontally). The regular UIView uses a subclass of UIPanGestureRecognizer to track vertical pans (Subclass found here). In the overlay view, I override the touch event methods to pass them to the UIScrollView.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[self.scrollView.panGestureRecognizer touchesBegan:touches withEvent:event];
[self.scrollView.singleTapGesture touchesBegan:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesCancelled:touches withEvent:event];
[self.scrollView.panGestureRecognizer touchesCancelled:touches withEvent:event];
[self.scrollView.singleTapGesture touchesCancelled:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self.scrollView.panGestureRecognizer touchesEnded:touches withEvent:event];
[self.scrollView.singleTapGesture touchesEnded:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
[self.scrollView.panGestureRecognizer touchesMoved:touches withEvent:event];
[self.scrollView.singleTapGesture touchesMoved:touches withEvent:event];
}
The overlay view, the vertical pan works perfectly. In the UIScrollView, the taps also work perfectly. The scrolling though, not so much. The scroll view scrolls about three points, then stops (as I continue with the horizontal pan.) If I let go with a velocity, the scroll view then picks up that velocity and finishes scrolling.
What would be the possible issues causing the scroll view to stop scrolling then pick up the velocity?
From the code you posted, I assume you are not doing anything with the transparent UIView.. then why don't you just set userInteractionEnabled = NO; ??
Here's an easier solutions that worked well for me:
In the transparent UIView (let's call it OverlayView) make the width and height both 0 (so that the view is no longer technically on top of the UIScrollView) and set clipsToBounds = NO (so that the contents of OverlayView still show up on top of the UIScrollView).
self.OverlayView.clipsToBounds = NO;
CGRect frame = self.OverlayView.frame;
self.OverlayView.frame = CGRectMake(frame.origin.x, frame.origin.y, 0, 0);
Note that if OverlayView contains interactive controls (like the button above) then they will no longer work. You'll need to move it into it's own view above the UIScrollView.
#import "CustomUiView.h"
#implementation CustomUiView.h
-(BOOL) pointInside:(CGPoint)point withEvent:(UIEvent *)event{
return NO;
}
#end
The view you do not want to interact.
create custom UIView class and use this custom view as your top view that return NO in pointInside method. Then the touch will go automatically underearth. In your case Your Transparent UIView will be CustomUiView subclass of UIView.

Capture and stop propagation of user interaction iOS UITableViewCell

I am doing some custom drawing a UITableViewCell that I have subclassed. In there there is an OpenGL ES 2.0 control that needs user interaction to work... now if I start dragging horizontally the control responds but as soon as I go in the vertical direction then it starts to move the table view's viewport. How do I stop this interaction from going to the UITableView and limit it to my own UIView in the UITableViewCell?
You can try to subclass UITableView and override following methods
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
// if user tapped on your custom view, disable scroll
self.scrollEnabled = NO;
// end if
[super touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
self.scrollEnabled = YES;
[super touchesEnded:touches withEvent:event];
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
self.scrollEnabled = YES;
[super touchesCancelled:touches withEvent:event];
}
I don't think you can prevent user interaction on the UITableView, since it would affect all the subviews.
I'll try to send the UITableView interaction to the method you want it to use it.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[MyClass myScrollDidStartMethod];
}
or use - (void)scrollViewDidScroll:(UIScrollView *)sender so that you can work on the sender's contentOffset to get the scrolling direction (see this post for more info about this)

Resources