Detect touches for 2 subviews - iphone

I have a UIView sitting atop a UIScrollView.
I'd like to be able to scroll my ScrollView normally while having my UIView catch:
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
The only issue is I cannot seem to get both subviews to detect touches at the same time. I can set the top one to userInteractionEnabled to NO. But that doesnt really help me get them both.
Any thoughts??
Thanks!

In one of my project I catch touches in one view and pass the method to a second one this way:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
// Do something here
[otherView touchesEnded:touches withEvent:event];
}
EDIT: You can also use a third UIView just to menage touches.
Put a third UIView on top of your views (it should be as wide as your screen) and let it menage your touches
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
// Get touch position
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:self.view];
// Use location to pass touches to one view ot the other
if (location == something) {
[oneView touchesEnded:touches withEvent:event];
} else {
[otherView touchesEnded:touches withEvent:event];
}
}

Related

UIScrollView touching and listening for events

I have an image inside of a UIScrollView and I have code which draws text as the user drags their finger around. I'd like to be able to control, when the user is touching the screen, if
a) the user should be using their finger to draw OR
b) the user should be moving the scroll view around with their finger
I have a boolean which keeps track of what the user is doing. And I have touches methods:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
if(draw == false) {
printf("calling super");
[scrollView touchesBegan:touches withEvent:event];
}
else
[myPath moveToPoint:[mytouch locationInView:self]];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
if(draw == false)
[scrollView touchesMoved:touches withEvent:event];
else {
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
Why doesn't this work? Basically, if I'm not drawing, I call the touchesBegan and touchesMoved of the scroll view. If I am drawing, I draw using myPath.
However, the scroll view is not moved around or zoomed in, etc, as it should be, when draw is false.
i have met this problem before. And i solve like this:
you should make a mainView . It has 2 property. One is yourScrollView , and One is yourDrawImageView. [yourScrollView addSubviews:yourDrawImageView];[mainView addSubviews:yourScrollView];
and then write your touches method in the mainView.m like this (ignor the scrollView statment)
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
if ([[touches allObjects] isKindOfClass:[yourDrawImageView class]])
{
[myPath moveToPoint:[mytouch locationInView:self]];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *mytouch=[[touches allObjects] objectAtIndex:0];
if ([[touches allObjects] isKindOfClass:[yourDrawImageView class]])
{
[myPath addLineToPoint:[mytouch locationInView:self]];
[self setNeedsDisplay];
}
}
The last step , whats you ignor is to code scrollView touches event , its write in yourSceollView.m,like this:
#import "yourScrollView.h"
#implementation yourScrollView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code.
}
return self;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
if(!self.dragging)
[[self nextResponder] touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
if(!self.dragging)
[[self nextResponder] touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesEnded:touches withEvent:event];
if(!self.dragging)
[[self nextResponder] touchesEnded:touches withEvent:event];
}
- (void)dealloc {
[super dealloc];
}
#end
I wish it can help you :-)
UIScrollView uses UIGestureRecognizers, which are called by the system separately from touchesBegan:withEvent: and friends. An easier way to do this is to simply disable scrolling on the scroll view when you don't want it to react to touches.

Re-issuing a touch event

I'm trying to create an overlay view that monitors for touches and then disappears, but also forwards touch events to whatever is underneath the view.
My test app has a view with a button inside. I add the overlay view as another subview (sibling to the button, essentially), which takes up the entire screen.
For both solutions I tried, the overlay keeps state to determine how it responds to a touch. When a touchesBegan event is received, the overlay will stop responding to hitTest or pointInside until it receives a touchesCancelled or touchesEnded.
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
if(_respondToTouch)
{
NSLog(#"Responding to hit test");
return [super hitTest:point withEvent:event];
}
NSLog(#"Ignoring hit test");
return nil;
}
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
if(_respondToTouch)
{
NSLog(#"Responding to point inside");
return [super pointInside:point withEvent:event];
}
NSLog(#"Ignoring point inside");
return NO;
}
For my first approach, I tried re-issuing the event:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if(!_respondToTouch)
{
NSLog(#"Ignoring touches began");
return;
}
NSLog(#"Responding to touches began");
_respondToTouch = NO;
[[UIApplication sharedApplication] sendEvent:event];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches cancelled");
_respondToTouch = YES;
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches ended");
_respondToTouch = YES;
}
However, the button didn't respond to the re-issued event.
My second approach was to use hitTest to discover the view (my button) underneath the overlay, and then send a touchesXXX message directly to it:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches began");
_respondToTouch = NO;
UITouch* touch = [touches anyObject];
_touchDelegate = [[UIApplication sharedApplication].keyWindow hitTest:[touch locationInView:self.superview] withEvent:event];
CGPoint locationInView = [touch locationInView:_touchDelegate];
NSLog(#"Sending touch %# to view %#. location in view = %f, %f", touch, _touchDelegate, locationInView.x, locationInView.y);
[_touchDelegate touchesBegan:touches withEvent:event];
}
- (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches cancelled");
[_touchDelegate touchesCancelled:touches withEvent:event];
_respondToTouch = YES;
}
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches ended");
[_touchDelegate touchesEnded:touches withEvent:event];
_respondToTouch = YES;
}
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touches moved");
[_touchDelegate touchesMoved:touches withEvent:event];
}
It finds the button (according to the logs), but the button doesn't react at all when I invoke touchesXXX on it.
I'm not sure what else to try since the button won't respond to a direct invocation of touchesBegan =/

Second UITouch Event trigger First UITouch Event why?

I have a great problem since last 2 days. I'm working with a multiple Touch enabled view. My UIViewController has 8-10 imageview. I want to detect touch on each view separately, but multiple view at a time. Touch is detected on all image view, but problem is here-
Suppose I have Tap on a image view and hold down this image view and now tap another image view, second image view is detected touch successfully but it also trigger to first image view which is previously touched and hold down. But I don't want it. So please any one help me. Code level help is appreciated.
NB. My UIViewController also implemented TouchesMoved Methods for swiping purpose.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *touch in event.allTouches) {
if(CGRectContainsPoint([imView1 frame], [touch locationInView:self.view])){
NSLog(#"imView 1 touched");
}
if(CGRectContainsPoint([imView2 frame], [touch locationInView:self.view])){
NSLog(#"imView 2 touched");
}
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for(UITouch *touch in event.allTouches) {
if(CGRectContainsPoint([imView1 frame], [touch locationInView:self.view])){
NSLog(#"imView 1 touch moved");
}
if(CGRectContainsPoint([imView2 frame], [touch locationInView:self.view])){
NSLog(#"imView 2 touch moved");
}
}
}
Try subclassing a UIImageView and handle the touches code in that subclass. If you need to pass the touch to the parent for some reason, you can handle the touch then send it on to the parent which would be your view controller.
EDIT:
have your custom class like this
#interface CustomImageView : UIImageView {}#end
#implementation CustomImageView
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touch began %d",self.tag);
[self.nextResponder touchesBegan:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touch moved %d",self.tag);
[self.nextResponder touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"touch ended %d",self.tag);
[self.nextResponder touchesEnded:touches withEvent:event];
}
#end
and have your parrent code something like this
#implementation trashmeTouchIpadViewController
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"parent Touch Began");
//do parent stuff
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"parent Touch ended");
//do parent stuff
}
- (void)viewDidLoad {
[super viewDidLoad];
[self.view setMultipleTouchEnabled:YES];
UIImage *i1 = [UIImage imageNamed:#"img1.jpg"];
CustomImageView *v1 = [[CustomImageView alloc] initWithImage:i1];
v1.frame = CGRectMake(100, 100, 200, 200);
v1.userInteractionEnabled = YES;
v1.multipleTouchEnabled = YES;
v1.tag = 111;
[self.view addSubview:v1];
[v1 release];
UIImage *i2 = [UIImage imageNamed:#"img2.jpg"];
CustomImageView *v2 = [[CustomImageView alloc] initWithImage:i2];
v2.frame = CGRectMake(500, 100, 200, 200);
v2.userInteractionEnabled = YES;
v2.multipleTouchEnabled = YES;
v2.tag = 999;
[self.view addSubview:v2];
[v2 release];
}

Programmatically pass touches to UIScrollView to scroll

Basically I'm trying to make UIScrollView only scroll on higher angles. As in right now if you move your finger 10 degrees off horizontal, the scrollview will scroll. I'd like to push that up to, say, 30 degrees.
After doing some reading, I established the best way to do this would be to put a subclassed UIView on top of the scrollview. If the UIView on top's touches are above 30 degrees, pass it down to the scrollview, and otherwise don't.
However, I can't figure out how to pass the touches down. Here's my code right now:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"glass touch began");
UITouch *touch = [touches anyObject];
beginning_touch_point = [touch locationInView:nil];
[scroll_view touchesBegan:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"glass touch ended");
UITouch *touch = [touches anyObject];
CGPoint previous_point = beginning_touch_point;
CGPoint current_point = [touch locationInView:nil];
float x_change = fabs(previous_point.x - current_point.x);
float y_change = fabs(previous_point.y - current_point.y);
if(x_change > y_change)
{
if(previous_point.x - current_point.x < 0)
{
[(MyScheduleViewController *)schedule_controller didFlickLeft];
}
else
{
[(MyScheduleViewController *)schedule_controller didFlickRight];
}
[scroll_view touchesCancelled:touches withEvent:event];
}
else
{
[scroll_view touchesEnded:touches withEvent:event];
}
}
I know right now that it's checking for 45 degrees, but that's not the important thing. What is important is that the touches are indeed getting passed down correctly to my scroll_view. I have it doing a NSLog() on touchesbegan and touchesended, and it's doing both correctly. It's just not scrolling. I'm worried touchesBegan and touchesEnded cannot cause a scroll. Does anyone know what can, or what I'm doing wrong?
I also tried to do the same but didn't succeed. It seems scrollView doesn't handle touchesMoved/touchesBegan, but it handles some other events to understand that user wants to scroll the view.
For me the solution was to determine thet shift value and set it explicitly as a scrollView content offset. It looked like this (I don't have the exact source code no, this code may work incorrectly):
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint curr = [touch locationInView: self];
CGPoint offset = [scrollView contentOffset];
[scrollView setContentOffset: CGPointMake(offset.x + (curr.x - prev.x), offset.y) animated:YES];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
prev = [[touches anyObject] previousLocationInView: self];
}

Successful Swipe in UITextView?

Has anyone been able to implement a successful swipe gesture on a UITableView? By default this is not possible due to the scrolling nature of the control. I've tried subclassing UITextView and implementing the swipe function in the instance methods, but no dice.
My UITextView has scrolling disabled - Unless there is another way to implement multiline text?
[Edit] What I should say is input multiline text[/Edit]
Here is a subclass of a UITextView that will detect a swipes gesture...
Is this what you are looking for?
#import <UIKit/UIKit.h>
#define kMinimumGestureLength 25
#define kMaximumVariance 5
#interface SwipeableTextView : UITextView {
CGPoint gestureStartPoint;
}
#end
#implementation SwipeableTextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch =[touches anyObject];
gestureStartPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance) {
NSLog(#"Horizontal swipe detected");
}
}
#end
An even better solution, I have found, is to simply pass touches back to the superview:
#interface SwipeableTextView : UITextView {
}
#end
#implementation SwipeableTextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
[self.superview touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
[self.superview touchesMoved:touches withEvent:event];
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
[self.superview touchesEnded:touches withEvent:event];
}
#end
Add a transparent UIView on top of the UITextView and use it to handle the swipe, and send the touchesBegan/Moved/Ended/Canceled: messages to the text view to preserve normal interaction.
I took Ross's code and added a few things that helped me. In particular, this code won't respond to the swipe until it stops. It also prevents a swipe from reversing direction partway through.
#import <UIKit/UIKit.h>
#define kMinimumGestureLength 25
#define kMaximumVariance 5
typedef enum swipeDirection {
kSwipeNone,
kSwipeLeft,
kSwipeRight
} tSwipeDirection;
#interface SwipeableTextView : UITextView {
CGPoint gestureStartPoint;
tSwipeDirection swipeDirection;
}
#end
#implementation SwipeableTextView
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
swipeDirection = kSwipeNone;
UITouch *touch =[touches anyObject];
gestureStartPoint = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x);
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y);
// Check if we already started a swipe in a particular direction
// Don't let the user reverse once they get going
if (deltaX >= kMinimumGestureLength && deltaY <= kMaximumVariance &&
swipeDirection == kSwipeNone) {
if (gestureStartPoint.x < currentPosition.x) {
swipeDirection = kSwipeRight;
}
else {
swipeDirection = kSwipeLeft;
}
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if (swipeDirection == kSwipeRight) {
NSLog(#"Swipe right");
}
else if (swipeDirection == kSwipeLeft) {
NSLog(#"Swipe left");
}
[super touchesEnded:touches withEvent:event];
}
#end
Usually you implement the touch responding events in a UITableViewCell, which can recognize the swipe and do something. UITableViews inherit from UIScrollView which does not like having touch events overridden.
You could also use a two finger swipe. When I get home I can elaborate with some code but that was what I have used