I have an UIViewController which contains a UITableView (subclassed) and another UIView (subclassed). They are on the same hierarchy level but the UIView is added last so it is the frontmost.
I overrid touchesBegan/Moved/Ended to intercept the Gestures from the top UIView: my goal is to get the selected UITableViewCell and, if double tapped, create an ImageView to be dragged around.
I appear to get it done but now I cannot scroll the UITableView anymore, even though I forward the touch events.
Here are the methods for the UIView:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BO");
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesBegan:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"Mo");
UITouch * touch = [touches anyObject];
CGPoint tPoint = [touch locationInView:self];
copyObj.center = tPoint;
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesMoved:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesMoved:touches withEvent:event];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
if ([touch tapCount] == 2)
{
[self desubCopyView];
}
CGPoint tPoint = [touch locationInView:self];
copyObj.center = tPoint;
InventoryViewController * invViewCont = self.viewController;
UITableView * invTab = invViewCont.inventoryTableView;
[invTab deselectRowAtIndexPath:[invTab indexPathForSelectedRow]
animated:YES];
NSArray * cells = [invTab visibleCells];
BOOL found = NO;
for (UITableViewCell * cell in cells)
{
if (CGRectContainsPoint(cell.frame, tPoint))
{
[cell touchesEnded:touches withEvent:event];
//[cell.imageView touchesEnded:touches withEvent:event];
found = YES;
break;
}
}
if (!found)
{
[invViewCont.inventoryTableView touchesEnded:touches withEvent:event];
}
}
And here are those in the UITableViewCell
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch * touch = [touches anyObject];
if ([touch tapCount] == 2)
{
CGPoint tPoint = [touch locationInView:self];
NSLog(#"CellX %lf CY %lf", tPoint.x, tPoint.y);
UIGraphicsBeginImageContext(self.bounds.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView * newView = [[UIImageView alloc] initWithImage:viewImage];
[dragArea addSubview:newView];
dragArea.copyObj = newView;
[newView release];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.4];
dragArea.copyObj.transform = CGAffineTransformMakeScale(1.3, 1.3);
[UIView commitAnimations];
tPoint = [self convertPoint:tPoint toView:dragArea];
dragArea.copyObj.center = tPoint;
}
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"MOV %#", self.imageView.image);
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"ENDED");
[super touchesEnded:touches withEvent:event];
}
And in my UITableView I have simply:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"BEGTB");
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"MOVTB");
[super touchesMoved:touches withEvent:event];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"ENDTB");
[super touchesEnded:touches withEvent:event];
}
I am surely missing something but I do not know what
I would highly recommend looking into UITapGestureRecognizer instead of handling the touchesX events yourself.
I found a workaround for this, override the methods for touch gestures in my custom UITableView in order to make it scroll programMatically as I drag upon it an object.
Here is the 'solution'.
I still believe there is another simpler way to do this but I did not find it, so posting this and marking it as an 'answer' might help someone else.
Related
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.
i am using this code in my app to recognize touch and drag on specific UIView,
now i have a problem that if i make the touch and immediate make a drag the touchesMoved called only 3-4 times and stop and then the touchesCancelled called, but if i touch the screen wait a second and then make a drag it call touchesMoved every time the finger move.
Edit
Ijust tested it on iphone 4s and with this device it work perfect,in ipod4 it still have the problem. both device are with 5.01 .
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSArray* touchArray = [touches allObjects];
UITouch* touch = [touchArray objectAtIndex:0];
CGPoint point = [touch locationInView:self.view];
UIView *tmp = [self.view hitTest:point withEvent:event];
if (tmp == volumeViewBackground) {
//do something
}else {
NSLog(#"err");
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSArray* touchArray = [touches allObjects];
UITouch* touch = [touchArray objectAtIndex:0];
CGPoint point = [touch locationInView:self.view];
UIView *tmp = [self.view hitTest:point withEvent:event];
if (tmp == volumeViewBackground) {
//do something
}else {
NSLog(#"err");
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"error");
}
Try calling super according to UIResponder Class Reference.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesBegan: touches withEvent: event];
NSArray* touchArray = [touches allObjects];
UITouch* touch = [touchArray objectAtIndex:0];
CGPoint point = [touch locationInView:self.view];
UIView *tmp = [self.view hitTest:point withEvent:event];
if (tmp == volumeViewBackground) {
//do something
}else {
NSLog(#"err");
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved: touches withEvent: event];
NSArray* touchArray = [touches allObjects];
UITouch* touch = [touchArray objectAtIndex:0];
CGPoint point = [touch locationInView:self.view];
UIView *tmp = [self.view hitTest:point withEvent:event];
if (tmp == volumeViewBackground) {
//do something
}else {
NSLog(#"err");
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesCancelled: touches withEvent: event];
NSLog(#"error");
}
When touch began-it shows the image,when touchmoved the image follows the touch events, when touch ended the image disappear
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesBegan:touches withEvent:event];
newgicg.hidden = NO;
NSArray *allTouches = [touches allObjects];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
int count = [allTouches count];
if (count == 1) {
newgicg.center = touchLocation;
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
newgicg.center = touchLocation;
[self.view addSubview:newgicg];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
newgicg.hidden = YES;
}
I want something like ,to follow the collection of stars when the cursor moved on the screen.i want the stars to be blinks and vanished wherever the cursor goes on the screen
- (void) touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[super touchesBegan:touches withEvent:event];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInView:touch.view];
UIImageView *imageView=[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"simple-apple.png"]];
imageView.center = touchLocation;
[self.view addSubview:imageView];
[imageView release];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesEnded:touches withEvent:event];
for (UIView *view in [self.view subviews]) {
[view removeFromSuperview];
}
}
try this code instead, just replace the name of the image.
EDIT:
one thing I forgot to mention is that you can keep a counter to the no. of subviews added this way and remove them , otherwise it may also remove other views which were there on the view before.
This is a beginner's question I'm afraid:
I have an UIText which covers the entire screen. I have another transparent view on top of this UITextView so as to be able to recognise swiping gestures (horizontally and vertically), like so:
- (void)viewDidLoad
{
[super viewDidLoad];
// UITextView
CGRect aFrame = CGRectMake(0, 0, 320, 480);
aTextView = [[UITextView alloc] initWithFrame:aFrame];
aTextView.text = #"Some sample text.";
[self.view addSubview:aTextView];
// canTouchMe
CGRect canTouchMeFrame = CGRectMake(0, 0, 320, 480);
canTouchMe = [[UIView alloc] initWithFrame:canTouchMeFrame];
[self.view addSubview:canTouchMe];
}
Let's consider the user touches (not swipes) the canTouchMe View. In this case, I would like the canTouchMe view to disappear and pass on the touch to the UITextView hiding beneath so that it enters the editing mode and enable the 'natural' scrolling options an UITextView has (i.e. only horizontally).
My touches began method looks like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
UITouch *touch =[touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
How do I tell this method that IF it recognises only ONE touch, that it should hide the canTouchMeFrame and PASS ON the touch to the UITextView?
Sorry if this is basic, but I have no idea how to implement this. Thanks for any suggestions.
EDIT:
I introduced a touchEnded method, but I still have no luck. The touch will not be forwarded to the UITextView. I need to tap twice in order to edit it:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
[super touchesMoved:touches withEvent:event];
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView:self.view];
CGFloat deltaX = fabsf(gestureStartPoint.x - currentPosition.x); // will always be positive
CGFloat deltaY = fabsf(gestureStartPoint.y - currentPosition.y); // will always be positive
if (deltaY == 0 && deltaX == 0) {
label.text = #"Touch"; [self performSelector:#selector(eraseText) withObject:nil afterDelay:2];
[aTextView touchesBegan:touches withEvent:event];
[self.view bringSubviewToFront:aTextView];
[self.view bringSubviewToFront:doneEdit];
}
}
NSSet has a -count method. If touches only has one object, then you're responding to a single touch.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[super touchesBegan:touches withEvent:event];
if ([touches count] == 1) {
[self hideMyRectangle];
[someOtherObject touchesBegan:touches withEvent:event];
//etc, etc.
return;
}
// if you get here, there's more than one touch.
UITouch *touch =[touches anyObject];
gestureStartPoint = [touch locationInView:self.view];
}
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];
}