iPhone: Button Touch Events - Release not Firing? - iphone

SOLVED - Please seen Darren's response for a working resolution to this issue.
Hello, and thank you for taking the time to read my question.
I am experiencing some odd behavior with detecting the release of an IBAction UIButton: When I move my palm across the interface, button press is detected, however release is not!
I have a set of controls, each with their own IBAction UIButton. Here is an example of how my buttons react and behave:
- (IBAction)up1start:(id)sender
{
if(!buttonIsPressed && deviceIsConnected)
{
NSLog(#"BUTTON UP 1 PRESSED!");
NSString *byteString = #"7E 01";
sendTimer =[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:#selector(sendByte:) userInfo:byteString repeats:YES];
buttonIsPressed = true;
}else{
NSLog(#"Cant press two buttons or connection is closed");
}
}
- (IBAction)up1stop:(id)sender
{
NSLog(#"BUTTON UP 1 RELEASED!");
[ self buttonRelease ];
}
I have the following events attached to UIButton up1 - likely more than necessary due to my ongoing troubleshooting efforts:
Touch Down - up1start
Touch Drag Exit - up1stop
Touch Drag Outside - up1stop
Touch Up Inside - up1stop
Touch Up Outside - up1stop
I'm a bit stumped here as to how none of these events result in my "up1stop" action being triggered. Again, I can reproduce this issue by pressing my palm against the interface and then releasing, or moving slightly. For the record, I have enabled exclusiveTouch for each button.
Everything else is working perfectly and I DO have to try to get this behavior to occur, however given the nature of the app it would be incredibly unsafe for users if this is not resolved.
Can anyone explain why the button release is not being detected when I 'palm' the screen?
Cheers!

You'll want to track the UIControlEventTouchCancel event.
A UIControlEventTouchCancel event occurs if the initial touch in reinterpreted as another gesture, such as a scroll.

Related

i need to reset the first view with a button press

on an app that i am creating, you need to slide a square through a maze and if it touches the wall, it goes to another screen telling you that you lose. However, when you hit the reset button, it goes back, and the square is in the exact same spot as where it touched the walls. Is there any way that i can press the button and have the square go back to start at the same time? Any feed back is appreciated! Thanks. (By the way, i am using Xcode.)
Here is the code for the button in the .h file:
- (IBAction)retry:(id)sender;
and here is the code for the .m:
- (IBAction)retry:(id)sender {
[self dismissModalViewControllerAnimated:YES];
}
Use viewWillAppear to reset your views
-(void) viewWillAppear: (BOOL) animated {
}
Call your initial method on buttonPressEvent.
- (IBAction)retry:(id)sender {
[self viewDidLoad];
}

Why is this UIButton still triggering event hander while disabled

I have a UIButton in a view. I attach an Event handler to it like this:
[self.button addTarget:self
action:#selector(button_touchUpInside:)
forControlEvents:UIControlEventTouchUpInside];
The handler looks like this:
-(void) button_touchUpInside:(id)sender
{
NSLog(#"%#", ((UIButton *)sender).enabled ? #"ENABLED" : #"DISABLED"); // Logs DISABLED
// Do stuff
}
I disable the button like this:
-(void)setEnabled:(BOOL)enabled
{
enabled_ = enabled;
self.button.enabled = enabled;
}
My problem is that even after I set enabled = NO on the button a TouchUpInside still triggers the handler. I can see in the handler that the button is disabled, however the handler is still triggered.
Please note that there are several ways of working around this - checking for button.enabled in the handler, #sanchitsingh's answer below etc. WHat I want to know is why this is happening.
From my personal experience, from this question, and others thread across the web, it seems that Apple's documentation about UIControl.enabled is incorrect, and that setting a UIControl disabled doesn't prevent it from getting touch events. It only neutralizes a few events such as (from memory, can't check now) click, touch down, and current action-triggering
events, so you effectively have to use UserInteractionEnabled property to really get rid of touch events.
Just check if there is any gesture involved in your code. That could cause a problem.
I think you should just use
button.enabled = NO;
button.userInteractionEnabled = NO;
Try this
button.userInteractionEnabled=NO;

How to remain pause after resume the game from background in cocos2d?

I have a toggle menu which toggle between the words "Pause" and "Resume" when it is pressed, which also pause and resume the whole game. This means when playing, the menu will be shown as "Pause" (tap here to pause), when pausing the menu will be shown as "Resume" (tap here to resume).
Here is the problem, if I tap the home button after I pause the game, then go back into it, it resumes itself and the pause menu is shown as "Resume". And this doesn't make sense to me. The best way I want is to pause the game whenever go into the background and resume from background. I look at the following methods, but they don't really work:
-(void)applicationWillResignActive:(UIApplication *)application{
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
}
-(void)applicationWillEnterBackground:(UIApplication*)application{
}
-(void)applicationDidEnterBackground:(UIApplication *)application {
}
I even just put CCLOG in all of those methods, but nothing has been called. Is there something I am need to put/declare before I use those methods?
Sorry, it is a bit too long to read. Hope you can help me. Thank you.
I'm not sure why, but for my experience (not cocos2d), there's no additional implementation if want apply those methods.
perhaps, you should try look at this.
link 1 & link 2
-(void)applicationWillResignActive:(UIApplication *)application{
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
}
you need custom your cocos2d CCDIRECTOR class. And get now display layer. then active it pause or resume function. All this must need some protocol.
These methods are invoked when the iOS forces your application in the background, or resumes execution of your application, ie they are signals you receive when an external event causes your application to go to background, or come back from it. You should not try to invoke them directly. There is no real relationship with a 'user created' menu like yours (like your Resume/Pause menu), unless you make the relationship explicit.
So , in the following method:
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(#"<%#>:applicationWillResignActive - received signal, pausing sharedDirector.",[self class]);
// here : place your code for forcing your menu in the 'Resume' state
// i am assuming some kind of change in a button, and
// a state variable of your own that define and control
// what it means to be 'paused' from your applications point of
// view
// then force the director to pause (animations, scheduling, touch, etc ...)
[[CCDirector sharedDirector] pause];
}
after, when the iOS hands you back control by placing your application in the forground as the running application:
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"<%#>:applicationDidBecomeActive - received signal, resuming sharedDirector.",[self class]);
[[CCDirector sharedDirector] resume];
}
You dont really need to do anything fancy here other than restart the CCDirector, since your menu is in the 'Resume' state, garanteed. When user presses Resume, you will start your game again and put the menu in the 'Pause' state.

Is it possible to differentiate between a long press and a tap on a button?

Can we call different actions / delegates in response to the two different events of
A tap on a UIButton
A tap-and-hold on a UIButton
?
Yes, it's reasonably easy to implement this using a UILongPressGestureRecognizer (on iPhone OS 3.2+). A long press will be handled by the gesture recognizer, and a short tap will pass through to the button's normal action.
For example, I subclassed UIButton and added the following method for specifying a long touch action to go along with a tap (longPressGestureRecognizer is an instance variable):
- (void)setLongTouchAction:(SEL)newValue
{
if (newValue == NULL)
{
[self removeGestureRecognizer:longPressGestureRecognizer];
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
}
else
{
[longPressGestureRecognizer release];
longPressGestureRecognizer = nil;
longPressGestureRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:[[self allTargets] anyObject] action:newValue];
[self addGestureRecognizer:longPressGestureRecognizer];
}
}
I then could do the following to set both short tap and long press actions that will be handled by the same target:
[undoButton addTarget:self action:#selector(performUndo:) forControlEvents:UIControlEventTouchUpInside];
[undoButton setLongTouchAction:#selector(showUndoOptions:)];
As you can see, this is useful for the undo buttons you see in title bars of many iPad applications.
Brad Larson's answer looks pretty good but here's another one that might give you a bit more flexibility/control of what you want or might want to do.
You subclass UIButton, you override the touchesBegan and touchesEnded methods so that when the user starts a touch you call
[self performSelector:#selector(detecetedLongTap) withObject:nil afterDelay:1.0];
and in the touchesEnded you call:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(detecetedLongTap) object:nil];
to cancel the event if the finger was lifted too soon.
You can get full code for this in this blog post:
http://www.isignmeout.com/adding-long-tap-functionality-uibutton/
The best solution I can think of, is to create another class, and subclass UIButton. Then on Interface Builder (if that's what you're using), you can set the button's class to the custom class you just created.
So in this new class, you have to override a method called
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
This is basically telling you that someone pressed down on your button. The touches is an NSSet and it holds all the information for all the fingers that are pressing down on the screen. If you only are interested in the one that's pressing on the button itself, you'll probably have something like:
NSSet *myTouches = [event touchesForView:self.view];
So now that you have the touches that correspond to your button, you have to find out how many times the user tapped on that button. You do that with something like:
NSUInteger numTaps = [[myTouches anyObject] tapCount];
If this number is 2, that means the user just double tapped your button. Now comes the next part. How do you know if the user is holding that button? Well when the user lets go of the screen, another method gets called. You'll need to override that one as well:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
This is where you know if the person has stopped touching the screen or if his finger is still on it. If his finger is still on it, then this event hasn't been called yet.
Now enough with the background
Here's my suggestion to you. I suggest you override the touchesBegan: method and check if the number of taps in the button is 2. If so, then start a timer that does what you need it to do, for as long as you need it to be done, and then on the touchesEnded: method, you'll go ahead and stop that timer, and this way you will have done whatever it is that you needed to do, for as long as you needed to do it OR as long as the user has held on to the button.
I hope this helps, obviously I didn't write the whole code for you, you'll have to experiment and research that stuff, but if you have any questions, I'll be happy to lend a helping hand :)

reliable way to get iPhone touch input?

I started using the MoveMe sample to get touch input working.
basically, I define these two callback functions to get my touch input:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for ( UITouch* touch in touches )
{
printf("touch down");
}
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
for ( UITouch* touch in touches )
{
printf("touch up");
}
}
This works fine, until you have more than 5 touches on the screen at once. then it stops working properly, you won't get the "touch down" message if there are more than 5 touches on the screen. what is even worse is that you won't reliably get all of the "touch up" messages until you have removed ALL your fingers from the screen.
If you touch with 6 fingers, then release 3, then touch again with the other 3 still down, you will get the "touch down" but if you release it, some times you get the "touch up" sometimes you don't.
This pretty much makes it impossible to track touches, and usually results in a touch getting 'stuck' permanently down, when passed to my Touch Manager.
Are there some better apis to use to get touch input? is there at very least a function you can call to reliably get whether the screen is currently touched or not? that way I could reset my manager when all fingers are released.
EDIT:
right, there must be something I'm missing. because currently the calculator does something I cannot do with those callbacks.
it only accepts one touch at a time, if there is more than one touch on the screen it "cancels" all touches, but it must keep track of them to know that there is "more than one" touch on the screen.
if I touch the screen the button goes down, now if I add another touch to the screen, the button releases, cool, not allowed more than one touch. now, if I add 4 more fingers to the screen, for a total of 6, the screen should break, and when I release those 6 fingers, the app shouldn't get any of the "up" callbacks. yet when I release all of them and touch again, the button depresses, so it knows I released all those fingers!! how??
The problem you have is that the iPhone and iPod touch only support up to five touches at the same time (being fingers still touching the screen). This is probably a hardware limit.
(As St3fan told you already.)
The system will cancel all touches if there are more than 5 at the same time:
touchesCancelled:withEvent:
(This is probably what causes the odd behavior with only some touches calling touchesEnded:withEvent:)
If you want to know if a touch ended and it ended because it was lifted then make sure to check the UITouch's phase property.
It stops working because 5 is the max amount of touches that the iPhone and iPod currently support. No way around that I'm afraid.