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.
Related
This question has been asked many times before and I think I've read most of the threads about it but I still can't find the reason for the behavior in my code.
I create my custom UIView (called BView) programmatically like this:
CGRect frame = [[UIScreen mainScreen] applicationFrame];
_view = [[BView alloc] initWithFrame:frame];
[[[[UIApplication sharedApplication] windows] objectAtIndex:0] addSubview:_view];
When initializing I set UserInteractionEnabled for both the view and the superview to yes and the background color to black, because I've read that some had these problems because their background was transparent:
[self setUserInteractionEnabled:YES];
[self.superview setUserInteractionEnabled:YES];
[self setBackgroundColor:[UIColor blackColor]];
Still the functions to receive touch events are not called:
/* Receive touch events: Touch began */
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
bRenderer::log("Touch began");
[...]
}
/* Receive touch events: Touch moved */
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesMoved:touches withEvent:event];
bRenderer::log("Touch moved");
[...]
}
/* Receive touch events: Touch ended */
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
bRenderer::log("Touch ended");
[...]
}
Somewhere in my project there is also a view controller that receives touch events without problems and I get no errors anywhere in my project. Yet it is quite important to me to receive the touch events in my view instead of the controller. I really don't get what could be wrong here.
Thank you very much for your help
i have a uiwebview and an uiview next to each other everone taking 50% of the screen width.
i can smoothly scroll the uiwebview - and i want to be able that if i scroll up/down on the uiview - that the uiwebview scrolls in the given direction.
already subclassed uiview - and trie'd to send the touchesEnded... events to the webview.scrollview.
.h
#interface UIViewScrollDispatcher : UIView
#property(nonatomic, assign) id scroller;
.m
#implementation UIViewScrollDispatcher
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event {
[self.scroller touchesCancelled:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[self.scroller touchesEnded:touches withEvent:event];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self.scroller touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"TOUCHHHH %#", self.scroller);
[self.scroller touchesMoved:touches withEvent:event];
}
using it like this:
UIViewScrollDispatcher * rightarea = [[UIViewScrollDispatcher alloc] initWithFrame:CGRectMake(0, 0, 300, 300)];
rightarea.backgroundColor=[UIColor redColor];
rightarea.scroller=self.webView.scrollView;
tried it with self.webView and self.webView.scrollView
doesn't work - any help?
btw: the NSLog inside the touchesMoved gets called
regards
ok - did it with uiscrollview subclass and sync the .contentOffset and .contentSize
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.
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]
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)