I put UIButton inside UITableViewCell in UITableView that is behind UIScrollView. I subclassed UIScrollView to forward touches to UITableView.
So method from UITableViewDelegate didSelectRow is calling properly. The problem is that UIButton inside table cell is not receiving TouchUpInside actions.
How can I solve this problem without deleting ScrollView over TableView?
EDIT:
I resolved this issue by detecting which view will receive touch. Here's the code:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UIView *hitView = [self hitTest:[(UITouch*)[[touches allObjects] objectAtIndex:0] locationInView:self] withEvent:event];
if ([hitView isKindOfClass:[UIButton class]]) {
[(UIButton*)hitView sendActionsForControlEvents:UIControlEventTouchUpInside];
}
[super touchesBegan:touches withEvent:event];
}
If you want to enable actions for bouth objects - UIScrollView and UIButton you should to implement custom hit test mechanism for ScrollView.
In your UIScrollView subclass override - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event method to make views behind ScrollView available for getting events.
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
return __touchesEnabled;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
_touchesEnabled = NO;
UIWindow *window = [UIApplication sharedApplication].delegate.window;
for(UITouch *touch in touches) {
CGPoint point = [touch locationInView:self];
point = [window convertPoint:point fromView:self];
UIView *view = [window hitTest:point withEvent:event];
[view touchesBegan:touches withEvent:event];
}
_touchesEnabled = YES;
}
It works for me
Since you have added your scroll view over the UIButton, all the touch actions will not be passed to the button.
[yourScrollView setUserInteractionEnabled:NO];
This may solve your problem.
Related
I've an UITextView into an UITableViewCell, but I can't scroll the text. When I try to scroll the textView, the tableView catch the touches.
There is any way to fix this?
PS: Subclass of UITableViewController
I'm thinking you can detect the object being touched and if it is a UITextView, then temporarily disable scrolling with UITableView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch = [touches anyObject];
//detect if touch is on UITextView
if ([touch.view isKindOfClass: UITextView.class]) {
yourTableView.scrollEnabled = NO;
}
}
Don't forget to re-enable scrolling of the UITableView afterwards in touchesEnded
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
yourTableView.scrollEnabled = YES;
}
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.
Facing some difficulties implementing draggable View. using monotouch, but I dont' think it is language-specific...
the problem details:
I have a View that handles TouchesBegan, TouchesMove and reposition itself (by changing Frame property). That work perfect!
once I'm adding ScrollView with disabled scrolling and paging, any touches made on the scrollview are not getting to the View that has to move. Setting UserInteraction to NO fixes moving problem, but none of the controls hosted inside the scroller responds any touch.
Any idea on how to have ScrollView and allow it's superview to receive all the touches when there's no scrolling events available?
You have to pass the touch to the subViews, this category for UIScrollView will do the job:
#implementation UIScrollView (FixedApi)
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
NSLog(#"touch view = %#", [[touch view].class description]);
if ([[[touch view].class description] isEqualToString:#"UITableView"]) {
//You have touched a UITableView
} else if ([[[touch view].class description] isEqualToString:#"UIView"]) {
//You have touched a UIView
} else {
//Ignore the category and return the touch to the UIScrollView.
[self.superview touchesBegan:touches withEvent:event]; // or 1 nextResponder, depends
[super touchesBegan:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ( !self.dragging ) [self.nextResponder.nextResponder touchesEnded:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
#end
You can drag the subview using PanGesture Action...
-(void)panGRAction {
if (panGR.state == UIGestureRecognizerStateBegan) {
UITouch *touch = [[event allTouches] anyObject];
NSLog(#"touch view = %#", [[touch view].class description]);
if ([[[touch view].class description] isEqualToString:#"UITableView"]) {
//You have touched a UITableView
} else if ([[[touch view].class description] isEqualToString:#"UIView"]) {
//You have touched a UIView
} else {
//Ignore the category and return the touch to the UIScrollView.
[self.superview touchesBegan:touches withEvent:event]; // or 1 nextResponder, depends
[super touchesBegan:touches withEvent:event];
}
} else if (panGR.state == UIGestureRecognizerStateEnded) {
if ( !self.dragging ) [self.nextResponder.nextResponder touchesEnded:touches withEvent:event];
[super touchesEnded:touches withEvent:event];
}
}
I have a UITextField on a UITableView and I am using a number keyboard however I want it to be dismissed when user clicks on anything but the UiTextField.
I have seen several solutions however there appears to not be one definitive answer. For example some talk about gestures and when I implement them they do not appear to work using the code below:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[[self view] endEditing:TRUE];
}
As you can see I am trying but there is not one way that seems to be working. Can someone guide me please?
Thanks
Usually, you will want to hit-test the touch against areas which should not dismiss the keyboard; but in general the requirement is to tell the currently-in-focus control to "resign" it's status as "firstResponder". It might look like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.userInput resignFirstResponder];
}
However, you may also want to consider a special gesture recognizer for this, so that you are not having to over-analyze the NSSet of touches in the long run (delegate to the GestureRecognizer the task of determining the difference between an actual "dismiss pleas!" tap and a "can I scroll this?" swipe.
You should use resignFirstResponder instead :
[textField resignFirstResponder];
Use the following code
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
// Note the '!':
if(![[touch view] class] isKindOfClass [UITableViewController class]]){
// It's not a bubble they touched, dismiss the keyboard:
[self.view endEditing:YES];
}
[super touchesBegan:touches withEvent:event];
}
or else
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
// Note the '!':
if(![[touch view] class] isKindOfClass [UITableViewController class]]){
// It's not a bubble they touched, dismiss the keyboard:
[textField resignFirstResponder];
}
[super touchesBegan:touches withEvent:event];
}
this helps in doing what you want
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)