Why is this UIButton still triggering event hander while disabled - iphone

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;

Related

disable the click event of button in iPhone

I am new to iPhone development.
In my application,
i want to disable the click event of button when it pressed first time by the user.
for that purpose
i tried
buttonname.Enable=FALSE;
But the image of button also goes washed like a disable.
i Want to show Image properly and button should be disabled, both at time.
what to do?
Please help me.
Thanks For Your Time.
Set buttonname.userInteractionEnabled = NO; This will disable the action of the buttonname but the UIButton will be shown. Only the clicked event will not work.
Use this code it may help you
[buttonName addTarget:self action:nil forControlEvents:UIControlEventTouchDown];
Try setting its userInteractionEnabled property to NO.

iPhone: Button Touch Events - Release not Firing?

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.

Temporarily Stop UIButton from responding

I have a button in my app that calls a long asynchronous function that we do not want to have called multiple times concurrently. However when I start mashing the button, it always sends as many messages as taps. I want to stop the button from responding while the function is running. I currently have a flag that stops execution of the function when the function is active in another thread, and the button is set to be hidden as soon as it enters this function, but these don't have any effect.
Thanks for any help!
Assuming:
Your button has an IBOutlet reference.
You have an IBAction on the button.
Then you can simply set the button's Enabled property to NO, and re-enabled when you receive notification from your ASYNC task that its done.
-(IBAction) buttonClicked {
[myButton setEnabled:NO];
//Do stuff
}
-(void) notificationHandlerMethodForAsyncTaskDone:(NSNotification *)notification {
[myButton setEnabled:YES];
//Do stuff
}
Inside the method that handle the touch event you can put disable the button:
- (void)handleTouch:(id)sender {
// Do your asynchronous call
[sender setEnabled:NO];
}
Instead of thinking about disabling the button why not make the screen inactive. Show some message like "Processing..." or a Spinner. That way the user will know the something is happening & at the same time your problem is solved.
DSActivityView is a good library for this.
You can use the enabled property to turn the button off:
[myButton setEnabled:NO]
Documentation

Tap and hold: a pain in the uiBUTTon

Excuse the bad pun, I'm creating a custom tab bar in my iPhone app using UIButtons. Everything works beautifully except that when I tap and hold a button, it doesn't select it until I release it. It's really bugging me, because a standard UITabBarItem is selected on touch down and it just feels wrong.
I've set the IBAction to "Touch Down" and my code is pretty simple. Am I doing something wrong?
-(IBAction)tab1Pressed:(id)sender
{
if (self.tab1.selected == NO) {
self.tab1.selected = YES;
self.tab2.selected = NO;
}
}
Got a solution! Added a UILongPressGestureRecognizer to each UIButton, instead of using IBActions. Worked like a charm!
In viewDidLoad:
tapAndHold = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapAndHold)];
[tapAndHold setMinimumPressDuration:0.01];
[self.myTabBarButton addGestureRecognizer:tapAndHold];
[tapAndHold release];
You should use another event for your button. The default one is "touch up inside". The action is triggered when you release the button.
Using "touch down" should do what you want.
You can try subclassing a UIView and implementing touchesBegan:withEvent: method.

Keeping a UISegmentedControl (among others) responsive

I have a segmented control being used as a toggle. When toggled, I switch a bunch of contents around in a table view which takes a tiny but noticeable amount of time (inserting/deleting sections in the table view, animating the change, etc). I want the segmented control to respond to the selection immediately. So in my action handling code for the segmented control's UIControlEventValueChanged event, I do the following:
- (IBAction)groupingChanged:(id)sender {
UISegmentedControl *seg = sender;
[tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex];
[self performSelectorOnMainThread:#selector(updateGrouping)
withObject:nil
waitUntilDone:NO];
}
Where updateGrouping is:
- (void)updateGrouping {
MXAssertMainThread();
[tableView beginUpdates];
... several table updates
[tableView endUpdates];
}
Setting waitUntilDone:NO allows the groupingChanged method to complete before the updateGrouping is called, but this doesn't seem to be sufficient to repaint the view. The segmented control sticks until the table is done updating, then it switches over.
So I tried modifying groupingChanged: to create a thread for the update like so:
- (void)delayed {
[self performSelectorOnMainThread:#selector(updateGrouping)
withObject:nil
waitUntilDone:NO];
}
- (IBAction)groupingChanged:(id)sender {
UISegmentedControl *seg = sender;
[tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex];
[self performSelectorInBackground:#selector(delayed) withObject:nil];
}
And this does work. The segmented control toggles instantly and the table soon follows. But I'm not at all confident of the result. Is it simply a side-effect of giving the main thread a reprieve while the new thread started up? Is this just how I need to queue updates to the UI? It's clearly hacky. I'm hoping someone has a better pattern they're following for this situation.
Think of it this way - everything you do is generally done on the main thread, including UI updates.
So in your original code, the code that updated the table view was reached before the code that did the UI update on the segment.
So, you were correct that giving the main thread a break allowed more time to complete the updates to the UI, because the main thread was allowed to complete the UI updates while the background thread handled the table updates.
An alternative you could try is to use performSelector:withObject:afterDelay with a 0.0 delay (which allows the main thread to process other things before proceeding with the selector). It may work where performSelectorOnMainThread did not, as that call may be more immediate even though they end up doing something very similar.
If you just want to make sure the segmented control gets repainted real quick, I probably wouldn't dive into threads.
Instead I would just set a timer with a low value like 0.1, and that should be sufficient enough to get the control updated without any noticeable delay to the user.
I've used this when I have a lot of work to do but need a quick UI update.
Still a little "hacky", but without the introduction of threads.
So...
- (IBAction)groupingChanged:(id)sender {
UISegmentedControl *seg = sender;
[tableModel toggleOn:[seg selectedSegmentIndex] == ToggleOnIndex];
[NSTimer scheduledTimerWithTimeInterval:0.1
target:self
selector:#selector(updateGrouping)
userInfo:nil
repeats:NO];
}
I hit the same problem and solved it by subclassing the UISegmentedControl to create a Delayed UISegmentedControl.
In the delayed control I overrode addTarget:action:forControlEvents: to capture the target & action. Then when the segment event occurs I run an NSTimer to launch the captured target & action after a set delay. The result is that the UI gets updated to display the segment clicked and I can use the DelayedUISegmentedControl like I would a UISegmentedControl:
// Follows all normal initialization patterns of UISegmentedControl
UISegmentedControl *segmentedControl = [[DelayedUISegmentedControl alloc]
initWithItems:[NSArray arrayWithObjects: #"First", #"Second", nil]];
// Adds a delay to the selector; default is 0.25
[segmentedControl addTarget:self action:#selector(segmentAction:)
forControlEvents:UIControlEventValueChanged];
If you want to download the control I've open sourced it on google code.