I have added a UITableView as a subview on my UITableView.
I have troubling 'passing through' touches on my UITableView (subview) to the view of my UIViewController (where my touchesBegan: method works as expected).
I have tried:
Subclassing my UITableView and passing touchesBegan: to the superview from there
pointInside: in superview
hitTest: in subclassed tableView
Also: Disabling userInteraction on my tableView works, but of course, I still want to maintain the cellDidSelectRow.. functionality.
My Code implementation in ViewController:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"touchesBegan popup");
if (!self.draggable) {
//[super touchesBegan:touches withEvent:event];
return;
}
UITouch *touch = [touches anyObject];
if (!_moving) {
//(touch.view == self || touch.view.superview == self) &&
_moving = YES;
_movingStartY = [touch locationInView:self.view].y;
NSLog(#"moving popup");
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.draggable) {
//[super touchesMoved:touches withEvent:event];
return;
}
if (_moving) {
NSLog(#"touchesMoved popup");
UITouch *touch = [touches anyObject];
float offset = [touch locationInView:self.view].y - _movingStartY;
if ([self.touchEventDelegate respondsToSelector:#selector(popupNavigationBar:touchDidMoveWithOffset:)]) {
[self.touchEventDelegate popupNavigationBar:self touchDidMoveWithOffset:offset];
}
}
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.draggable) {
//[super touchesCancelled:touches withEvent:event];
return;
}
if (_moving) {
UITouch *touch = [touches anyObject];
float offset = [touch locationInView:self.view].y - _movingStartY;
[self movingDidEndWithOffset:offset];
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.draggable) {
//[self touchesEnded:touches withEvent:event];
return;
}
if (_moving) {
UITouch *touch = [touches anyObject];
float offset = [touch locationInView:self.view].y - _movingStartY;
[self movingDidEndWithOffset:offset];
}
}
- (void)movingDidEndWithOffset:(float)offset
{
_moving = NO;
if ([self.touchEventDelegate respondsToSelector:#selector(popupNavigationBar:touchDidEndWithOffset:)]) {
[self.touchEventDelegate popupNavigationBar:self touchDidEndWithOffset:offset];
}
}
How can I accomplish this?
Related
So... I am working on this small Mario-like game for iOS and have run into a problem that I cannot seem to fix.
When using my controls (see the screenshot) they sometime get "stuck" as in, the iOS device did not notice that I let go of a button. This causes the player to keep on moving in a direction after you have let go of the button.
Edit: It seems that the malfunction happens when I click two buttons at the exact same time.
Hopefully, someone will be able to spot what I can do differently in the following code:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
[self touchStateChanged:touchLocation buttonState:YES];
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint prevTouchLocation = [touch previousLocationInNode:self];
[self touchStateChanged:prevTouchLocation buttonState:NO];
CGPoint touchLocation = [touch locationInNode:self];
[self touchStateChanged:touchLocation buttonState:YES];
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
// Some other logic... {...}
[self touchStateChanged:touchLocation buttonState:NO];
}
}
-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches) {
CGPoint touchLocation = [touch locationInNode:self];
[self touchStateChanged:touchLocation buttonState:NO];
}
}
-(void)touchStateChanged:(CGPoint)location buttonState:(BOOL)state
{
SKNode *n = [self nodeAtPoint:location];
if ([n.name isEqualToString:#"LeftMove"]) {
self.player.moveLeft = state;
}
else if ([n.name isEqualToString:#"RightMove"]) {
self.player.moveRight = state;
}
else if ([n.name isEqualToString:#"Jump"]) {
self.player.didJump = state;
}
}
Screenshot showing buttons:
Thanks :-)
Partly Solved...
Current solution:
Added an instance variable called _touchEvent of type UIEvent and added the following line to touchesEnded:
_touchEvent = event;
Added the following code to my update method:
if (_touchEvent && (int)[[_touchEvent allTouches]count] == 0) {
self.player.moveLeft = NO;
self.player.moveRight = NO;
self.player.didJump = NO;
}
if (_touchEvent && (int)[[_touchEvent allTouches]count] == 1) {
for (UITouch *touch in [_touchEvent allTouches]) {
SKNode *n = [self nodeAtPoint:[touch locationInNode:self]];
if ([n.name isEqualToString:#"Jump"]) {
self.player.moveLeft = NO;
self.player.moveRight = NO;
}
}
}
I think the problem is the location of touch (in -(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event method) is outside any node.
Try to implement something like this
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for (SKNode *node in yourButtonsArray) {
// Some other logic... {...}
[node setState:NO];
}
}
Or you should take a look at KKButtonBehaviour from KoboldKit framework.
Here is my code in moving the image randomly and for the touch code.
-(void)viewDidAppear:(BOOL)animated {
timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:#selector(randomizeXandY) userInfo:nil repeats:YES];
}
-(void)randomizeXandY {
CGFloat x = (CGFloat) (arc4random() % (int) self.view.bounds.size.width);
CGFloat y = (CGFloat) (arc4random() % (int) self.view.bounds.size.height);
[self moveObjectsAtX:x Y:y];
}
-(void)moveObjectsAtX:(CGFloat)x Y:(CGFloat)y {
[UIView animateWithDuration:5 animations:^{
imgView.center = CGPointMake(x, y);
}];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches]anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if ([touch view] == imgView) {
imgView.center = touchLocation;
}
}
Try the code in touchesBegin and touchesEnded
-(void)touchesBegin:(NSSet *)touches withEvent:(UIEvent *)event {
// stop timers here
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
// start timers here again
}
I would rather suggest to use UIPanGestureRecognizer. In it's handling method you can check possible states of the touch.
-(void)handlePan:(UIPanGestureRecognizer *)recognizer {
switch (recognizer.state) {
case UIGestureRecognizerStateBegan: {
CGPoint touchLocation = [recognizer locationInView:self.view];
...
break;
}
case UIGestureRecognizerStateChanged:
...
break;
case UIGestureRecognizerStateEnded:
...
break;
default:
break;
}
You have to use these methods:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
//...
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
//..
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
//...
}
in touchesBegan method, check if the touch is on your image:
if (CGRectContainsPoint(yourImage.frame, touchLocation)){
dragging = YES;
}
if it yes set a global variable, for example bool dragging to YES.
in touchesMoved:
check
if (dragging){
yourImage.frame = CGRectMake(touchLocation.x, touchLocation.y,yourImage.frame.size.widht,yourImage.frame.size.height);
}
in touchesEnded:
set dragging to NO
dragging = NO;
So :
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (CGRectContainsPoint(yourImage.frame, touchLocation)){
dragging = YES;
[timer invalidate];
timer = nil;
}
//...
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
if (dragging){
yourImage.frame = CGRectMake(touchLocation.x, touchLocation.y,yourImage.frame.size.widht,yourImage.frame.size.height);
}
//..
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self.view];
dragging = NO;
//...
}
i have an image view as background image.
I am seeking that in some place on image view touches would be enabled.
I started with this:
- (id)initWithTouchPoint:(CGRect )point
{
self = [super init];
if (self) {
touchFrame = point;
[self setAccessibilityFrame:touchFrame];
}
return self;
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
-(BOOL)canResignFirstResponder{
return YES;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
if (CGRectContainsPoint(touchFrame, touchLocation)) {
//[self setUserInteractionEnabled:NO];
}else{
//[self setUserInteractionEnabled:YES];
}
DLog(#"touchesBegan at x : %f y : %f",touchLocation.x,touchLocation.y);
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
}
Is it possible to let user touch over image view when user touches in touchFrame ?
Thank you.
Add UITapGestureRecognizer on UIImageView
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(handleGesture:)];
[gesture setNumberOfTapsRequired:1];
[imageView setUserInteractionEnabled:YES];
[imageView addGestureRecognizer:gesture];
Now within the HandleGesture Method :
-(void)handleGesture:(UITapGestureRecognizer *)_gesture
{
if (_gesture.state == UIGestureRecognizerStateEnded)
{
CGPoint touchedPoint = [_gesture locationInView:self.view];
}
}
you can check now wether the touchedPoint within the handleGesture method are in the specified area or not and you can perform your desired task accordingly
You can try having a boolean variable as a class member say BOOL allowTouch initialised with value NO:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [[event allTouches] anyObject];
CGPoint touchLocation = [touch locationInView:self];
if (CGRectContainsPoint(touchFrame, touchLocation)) {
allowTouch = YES;
}
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
if(allowTouch)
{
//handle moves here
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
allowTouch = NO;//you can put your condition too to end touches
}
It may help.
I found this code on a tutorial which pretty much sets a left or right side BOOL to YES if the touch started on a specific side of the screen and then checks when the touch moves to see if it changes sides on the screen in order to make the other BOOL yes.
So I am now trying to implement multi-touch but I am not sure how it would work with the following code? Does anyone have any idea how I would go upon it?
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
touchStartPoint = [touch locationInView:self.view];
if (touchStartPoint.x < 160.0) {
touchLeftDown = TRUE;
}
else if (touchStartPoint.x > 160.0) {
touchRightDown = TRUE;
}
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentTouchPoint = [touch locationInView:self.view];
if (touchStartPoint.x < 160.0 && currentTouchPoint.x < 160.0) {
touchLeftDown = TRUE;
}
else if (touchStartPoint.x > 160.0 && currentTouchPoint.x > 160.0)
{
touchRightDown = TRUE;
}
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLeftDown = touchRightDown = FALSE;
}
Thanks!
Edit1:
These are what the bools are doing in the game loop, pretty much what I am trying to achieve is that if there is a touch on both sides at the same time, the TOUCH_INCREMENT will be 0 since the touches on each side will cancel each other out. How would I achieve that? Anyway this is the code I am talking about:
if (touchLeftDown == TRUE) {
touchStep -= TOUCH_INCREMENT;
}
else if (touchRightDown == TRUE) {
touchStep += TOUCH_INCREMENT;
}
else {
touchStep = SLOWDOWN_FACTOR * touchStep;
}
touchStep = MIN(MAX(touchStep, -MAX_ABS_X_STEP), MAX_ABS_X_STEP);
pos.x += touchStep;
You can probably make this work without the touchStartPoint (i)var. The important thing is to not use -anyObject and instead to inspect each touch. The following code modifications might work for you:
-(void) countTouches:(NSSet *)touches withEvent:(UIEvent *)event{
int leftTouches=0;
int rightTouches=0;
for (UITouch *touch in touches)
{
CGPoint location = [touch locationInView:touch.view];
//just in case some code uses touchStartPoint
touchStartPoint=location;
if (location.x < 160.0) {
leftTouches++;
}
else if (location.x > 160.0) {
rightTouches++;
}
}
//reset touch state
touchLeftDown=FALSE;
//set touch state if touch found
if(leftTouches>0){
touchLeftDown=TRUE;
}
touchRightDown=FALSE;
if(rightTouches>0){
touchRightDown=TRUE;
}
}
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self countTouches:touches withEvent:event];
}
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[self countTouches:touches withEvent:event];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
touchLeftDown = touchRightDown = FALSE;
}
Here I have created a function that is called by touchesBegan and touchesMoved because they need to implement the same logic. You may see some unintended side effects if touchStartPoint is being used in the code somehow.
Hello I have 2 UIImageViews in UIView.
Once I touch on UIImageView touchesBegan method gets called. But once I drag on UIImageView then touchesMoved is called. But at the same time touchesMoved for the second UIImageView is also called.
Can you please help me how i can get touchesMoved event for both the UIImageViews?
This is my code
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
if (CGRectContainsPoint(iv1.frame,currentPoint)==YES)
NSLog(#"iv1 Begin");
if (CGRectContainsPoint(iv2.frame,currentPoint)==YES)
NSLog(#"iv2 Begin");
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
if(CGRectContainsPoint(iv1.frame,currentPoint)==YES)
NSLog(#"iv1 Moved NO");
if(CGRectContainsPoint(iv2.frame,currentPoint)==NO)
NSLog(#"iv1 Moved YES");
if(CGRectContainsPoint(iv2.frame,currentPoint)==YES)
NSLog(#"iv2 Moved NO");
if(CGRectContainsPoint(iv2.frame,currentPoint)==NO)
NSLog(#"iv2 Moved NO");
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPoint = [touch locationInView:self.view];
if (CGRectContainsPoint(iv1.frame,currentPoint)==YES)
NSLog(#"iv1 End");
if (CGRectContainsPoint(iv1.frame,currentPoint)==YES)
NSLog(#"iv2 End");
}
You can use two outlets linked to two views and this is the code for reacognize the two view inside touch methods:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *t = [touches anyObject];
touchedView = t.view;
if (t.view == view1) {
//todo something with view1;
} else if (t.view == view2) {
//todo something with view2
}
}