Temporarily Stop UIButton from responding - iphone

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

Related

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;

Back button seems disabled iOS

In iOs I navigate (pushed) with a navigation controller, and in the viewDidLoad launch a request. This request take might take a long time, and I'd like the users will be able to go back if they won't want wait for the request.
The problem it's that the back button added in the navigation bar seems to be blocked until de request finishes. The button remember the user interaction and when the request finishes go back automatically.
The request has a delegate method for the response and when the app enters on that method the button wake up and go back.
If I touch during a request the touch/click effect don't appear on the button, and it don't either at the end of the request. When I wait until the end, the button had the normal effect for a touched/clicked button.
For that you can enable the button property like this.
[button setEnable:Yes];
[button setEnable:Yes];
also use this
[button setuserintractionEnable:no];
call your request in backgroundthread as
[self performSelectorInBackground:#selector(startRequest) withObject:nil];
and remember this always for core data too. this is that your UI will be rleased and the request will continue to work in background thread
and then implement request cancel on back button
initialize obj as
Obj *obj = [[Obj alloc] initWithDelegate:self selector:#selector(requestFinished)];
[self performSelectorInBackground:#selector(startRequest:) withObject:obj];
obj has the attributes SEL selector, and id delegate;
when request finishes
check
if ([obj.delegate respondsToSelector:obj.selctor])
{
[obj.delegat performSelector:obj.selector];
}
inform me if you need more specs

Xcode Add Delay So Button Press Can Be Registered

So I have an application that takes in images from the video camera and displays information to the screen after some processing on the image. I've added a pause button so the user can 'freeze' the most recent results on the screen.
The pause works, but it is one cycle behind. By the time the button press is recorded, the next image is already being processed so the results that are being locked on the screen are actually from the subsequent image. I've tried adding a delay to allow the user time to press pause using:
[self performSelector:#selector(waitForPause) withObject:nil afterDelay:2];
but this does not perform as I was expecting. Any ideas on how to handle this pause correctly?
EDIT: I've noticed that the AppDelegate file has some functions that appear to handle this sort of thing. Namely, applicationWillResignActive can supposedly be used to pause a game based on the apple docs. Has anyone used this for such a purpose? The method is currently empty. What would I have to add to this method to get it to temporarily pause the app based on a button press?
UPDATE: Here is how I currently implement and handle the pause button press.
//Pause Button
self.pauseButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[pauseButton addTarget:self
action:#selector(pauseButtonPress:)
forControlEvents:UIControlEventTouchDown];
[pauseButton setTitle:#"Pause" forState:UIControlStateNormal];
pauseButton.frame = CGRectMake(80.0, 100.0, 160.0, 40.0);
[self.view addSubview:pauseButton];
- (void) pauseButtonPress:(id) sender {
[pauseButton setHidden: YES];
[playButton setHidden: NO];
continueRunningScript = NO;
NSLog(#"paused");
NSLog(#"Current Result: %#", result_string);
}
Thanks.
You don't need to implement delays. Try this:
Each time you render a new image to the screen, save the old image in a previousImage variable. When the user hits the pause button, simply swap out what is currently on-screen for with the contents of previousImage.

how can i fire button action event without any user action iphone xcode?

how can i fire button action event without any user action?
pls give me any sample..
UIButtons are subclass of UIControl. They use the sendActionsForControlEvents: method to send out events. You can call that method on the button (or control) to have the actions sent.
[theButton sendActionsForControlEvents: UIControlEventTouchUpInside];
If user dont want to fire action on button,why do you show button,without showing button,u can do action.I think u need dynamic code (without interface builder),the code is
[button1 addTarget:self action:#selector(action1:) forControlEvents:UIControlEventTouchUpInside];
-(void) action1:(id)sender;
{
}
hope this will help....

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.