Touch events of Scrollview in subview of UITableviewCell in iOS - iphone

I have tried searching over the internet about this problem but not able to come up with the solution that solves my problem.
I have make a subclass of UITableViewCell in which I added UIScrollView as a subview, now I want scroll gestures to be listened by scrolView and single tap gesture is listened by UItableView. Also using this layout is against the HIG of Apple

This is an old question but since I didn't really find a good answer I figured I'd offer one. This approach captures the tap events on both the UIScrollView (contained 'within' the table cell) AND tap events on the UITableView cells directly, and passes them along to the UITableViewDelegate. It also allows you to scroll your UIScrollView content without passing the scroll events along. Doesn't require any subclassing.
Within ViewDidLoad, instantiate a UITapGestureRecognizer on the UITableView
// Capture taps on scrollView fields and pass along to tableView
let tap = UITapGestureRecognizer(target: self, action: "tapIntercept:")
tableView.addGestureRecognizer(tap)
Create a callback function for the selector above
// Get the location of the table cell beneath the scrollView and pass the event off to the tableView
func tapIntercept(recognizer: UIGestureRecognizer) {
let point = recognizer.locationInView(tableView)
if let indexPath = tableView.indexPathForRowAtPoint(point) {
tableView(tableView, didSelectRowAtIndexPath: indexPath)
}
}

If the scroll view is a subview of the table view, you can capture the gesture and send it to its parent view like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesBegan:touches withEvent:event];
}

Related

TouchesBegan not triggered in UIView as subview of UITableViewCell

I'm creating a custom dropdown menu inside a cell of a UITableView adding this menu as a subview of the cell content view.
When I click on the menu inside the cell I enlarge the view to show all the menu items: now the menu view is over the tableView and all cells.
So, when I select on of the items inside my custom view, the TouchesBegan it's triggered only in the part of the view that's over the UITableView cell in which I call the menu and not in all the view that I enlarged.
If I touch the view in part that is over another cell, the TouchesBegan it's triggered in the cell of the table view and not in the view on top of all cells.
Any suggestion?
Views typically don't respond to touch events on views or portions of views drawn outside its bounds. There are a few possible options to fix the issue:
Enlarge the cell when the dropdown menu is visible to fit the entire menu, but this might negatively impact the visual effect depending on your design.
Add the menu on the controller instead of the cell view. If you got this route, you can use the UIScrollViewDelegate events from the table view to reposition the menu as the user scrolls the table. Remember to account for the case where the menu is displayed on the last cell and might overdraw the controller's bounds if always drawn in downward direction.
Override hitTest:withEvent: and pointInside:withEvent on both your menu view and its superview to inform the OS that you respond to events even outside its normal bounds.
The following worked for me:
Subclass your cell and connect the root view of your layout with the cell:
#interface CustomCell : UITableViewCell
#property (weak, nonatomic) IBOutlet UIView *rootView;
#end
Override hitTest of the cell like this:
#implementation CustomCell
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
BOOL res = [self.rootView pointInside:point withEvent:event];
if(res){
return [self.rootView hitTest:point withEvent:event];
}
return [super hitTest:point withEvent:event];
}
#end
Now touchesBegin/Move/End/Cancel will be delivered to the view and it's hierarchy whenever touch is inside that view

how to know uitableview is pressed when empty

i am making a chat application, and in the chat window there are uilabels in uitableviewcells. initially the keyboard will be present but when the user touches on any place on uitableview, i will make the chat window as fullscreen (dissappearing keyboard).
i cant find a way/trick to accomplish this.
i have tried the following method: by using tableview:didselectrowatindexpath, i am able to do it but, user needs to press on an existent uitableviewcell. but i want to understand the press even when uitableview is empty..
note: my chat tableview is interactive e.x. some rows will include image button which need to be pressable, so i cant just put an invisible button onto uitableview.
thank you for your thoughts
Aytunc Isseven
What you want to do is add a gesture recognizer to the UITableView that responds to the appropriate gestures. I would recommend against using UITapGestureRecognizer as the UITableView is already using taps for selecting the cells, so you might want to try the UILongPressGestureRecognizer. I put together a small sample of how you can do this as follows:
In my viewDidLoad I did the following:
UILongPressGestureRecognizer *gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleLongPressFrom:)];
[self.tableView addGestureRecognizer:gesture];
and the handleLongPressFrom: is as follows:
- (void)handleLongPressFrom:(UILongPressGestureRecognizer *)recognizer {
NSLog(#"handleLongPressFrom: %#", recognizer);
// Add real code here
}
The full list of gestures can be found here.
Oh, if you did want to still use tap, check out this stack overflow question. I don't know if the method presented works fully, but it'd be a good place to start.
Using UITapGestureRecognizer with a UITableView:
Okay, since the tap gesture seems to be the correct one for your use case you can try and do the following. Step 1 is to set up the gesture recognizer as I listed above using the tap gesture instead of the long press gesture.
The code in viewDidLoad is very similar with an important addition ...
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapFrom:)];
// we need to set the gesture delegate so we can allow the tap to pass through to the
// UITableViewCell if necessary.
gesture.delegate = self;
[self.tableView addGestureRecognizer:gesture];
The handleTapFrom: function is pretty much the same with just the different gesture recognizer as the parameter.
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
NSLog(#"handleTapFrom: %#", recognizer);
// Add real code here
}
The major changes to this approach is that we need to implement the UIGestureRecognizerDelegate protocol. Since our goal is to allow the tap gesture to pass through the UITableView to it's subviews (i.e. the UITableViewCell and it's components) we need to implement the gestureRecognizer:shouldRecieveTouch: function. The following implementation should cover what you are attempting.
#pragma mark UIGestureRecognizerDelegate methods
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
// If the view that is touched is not the view associated with this view's table view, but
// is one of the sub-views, we should not recognize the touch.
if (touch.view != self.tableView && [touch.view isDescendantOfView:self.tableView]) {
return NO;
}
return YES;
}
The isDescendantOfView: function returns YES if the view it is testing against is the same as the view doing the testing, so we need to accommodate that case separately. You can generify this function by using gestureRecognizer.view instead of self.tableView, but I didn't think it was necessary in this case.
The trick is to make your viewController put a tap recognizer on the view but make it always opt out by returning NO from the delegate method "gestureRecognizerShouldBegin". That way gestureRecognizerShouldBegin gets called for every touch on the view, but you don't interfere with the normal event handling of the table.
- (void)viewDidLoad {
[super viewDidLoad];
[self detectTouchesOnView:self.tableView];
}
- (void)detectTouchesOnView:(UIView*)theView {
UITapGestureRecognizer* tapR = [[UITapGestureRecognizer alloc]initWithTarget:nil action:nil];
tapR.delegate = self;
[theView addGestureRecognizer:tapR];
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
// React to the UITableView being touched E.G. by hiding the keyboard as below.
[self.view endEditing:YES];
return NO;
}

touch through uiviews

I've got a rootView. Now I add a subview to my rootView by using the method addSubView:. After that the subview is added consisting of a view containing several buttons. Now what I want is, to be able to press both, the buttons on my rootView and the buttons on my subView. However when I turn off user interaction of my subView, I can't press its buttons any more. However if I let it on, I cant press the buttons of my rootView.
Can anyone help me?
For your "subview," subclass UIView and override the the hitTest method like this:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *subview = [super hitTest:point withEvent:event];
if ( subview != self )
return subview;
else
return nil;
}
This will cause the buttons and other views within your "subview" to respond to events, but the view itself will act as if it's not there.

Override UITextView's UIScrollView Method touchEnded

I've a UITextView (from the InterfaceBuilder) in my View. This contains a UIScrollView.
Now I have to detect touch Events inside the UITextView beacause I must close my own submenus.
The Apple UITextView is in a UIScrollView, and to detect a touch in a UIScrollView I've to ovveride the UITouch Funktion of UITextView's UIScrollView.
Any suggestions how I can do this?
UITextView is a subclass of UIScrollView. Is this what you mean when you say "contains a UIScrollView?"
There are a couple of approaches you could take here. If the touch you are concerned with is the first touch in the text view, and is therefore beginning editing, you can become its delegate and implement this method:
- (void)textViewDidBeginEditing:(UITextView *)textView
If you need to be aware of any tap that occurs inside the text view, not just initial editing taps, you can use a UITapGestureRecognizer to listen for taps. Something like this:
// in the method where you configure your view
UITapGestureRecognizer *tap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(textViewTapped:)] autorelease];
// assuming textView is pointing to your UITextView
[textView addGestureRecognizer:tap];
And then implement the action method elsewhere in your class:
- (void)textViewTapped:(id)sender {
// dismiss your menu or whatever
}
Note that I haven't actually tested this scenario, but it should work. Gesture recognizers are awesome.

Pass a tap from UIScrollView to its parent view

I have a custom UITableViewCell that has a UIScrollView in it that covers the entire cell. I want to be able to use the scroll view to scroll a label in the cell (which is working) but the scroll view seems to be 'stealing' the cell's tap event. So I was wondering:
How do you pass a touch event from a UIScrollView to its parent UITableViewCell?
In the UISCrollViewController, have you tried passing the touchesEnded on to super, assuming a scroll is not in progress?
- (void)touchesEnded:(NSSet *)aTouches withEvent:(UIEvent *)anEvent
{
if(![self isDragging])
{
[super touchesEnded:aTouches withEvent:anEvent];
}
}