Button Touch Only Works Once - iphone

For some reason, interaction with my UIButton only works once. I've tried both via IBAction, and by IBOutlet with "addTarget". I have no idea why.
Context:
BaseViewController
- (IBAction) button_touched:(id)sender; //<-- Declared here, but not implemented
- (void)userInputReceived:(BOOL)bSuccess; //<-- Declared here, but not implemented
ViewController1 : BaseViewController
- (IBAction) button_touched:(id)sender; //<-- Implemented here
- (void)userInputReceived:(BOOL)bSuccess; //<-- Implemented here
Also, this is where I try "addTarget" but that doesn't work either (first touch works, but not the second)
In the "button_touched" method of ViewController1 (vc1), I make a call to another class like this:
[someOtherClassObject doSomethingWithMyView:self];
That class simply pops up a message box, gets user input, then calls back on the viewcontroller:
(Inside SomeOtherClass):
-(void)doSomethingWithMyView:(BaseViewController*)vc
{
// Do Something
[vc userInputReceived:TRUE];
}
Once this workflow has executed once, the button touch never calls the "button_touch" method again. No matter what I do, I can't get this to be called again.
In a thread on one of the popular BBS forums, someone mentioned that a problem like this could be caused by not having the object (vc1) you think you do, but rather, another instance of it. So, I logged the instance like NSLog( "instance: %p", self) in numerous places, and it's always the same.
Any ideas are greatly appreciated. This is very frustrating.

shot in the dark maybe, but its possible that your view controller's view has lost first responder status due to the displaying of the message inside the other object (what type of object is this and how is the message displayed?).
Try after your statement:
[vc userInputReceived:TRUE];
adding the line
[[UIApplication sharedApplication].keyWindow makeFirstResponder:vc.view];

Related

Programmatically dismissing a UIAlertView on iOS 5 doesn't call didDismiss delegate method

I'm running into a problem where 9 times out of ten, when I call UIAlertView's dismissWithClickedButtonIndex:animated:, the delegate method alertView:willDismissWithButtonIndex: is not called. Is anyone else running into this problem? I'm about to file a bug with Apple but I'm curious to see if anyone else has run into this issue and figured out any workarounds.
To ensure a consistent behavior across iOS4 and 5, you could just remove the UIAlertView's delegate just prior to calling its dismissWithClickedButtonIndex:animated: method, then manually invoke the delegate method. e.g.
- (void)somethingDidHappen {
id<UIAlertViewDelegate> delegate = myAlertView.delegate;
myAlertView.delegate = nil;
// now, we know the delegate won't be called...
[myAlertView dismissWithClickedButtonIndex:0 animated:NO];
// ...so we call it ourselves below
[delegate alertView:myAlertView clickedButtonAtIndex:0];
}
(That code isn't tested, but you get the point.)
Delegates of UI objects are only called when the user performs an action. Apple assumes that when you do something from code, you already know what you're doing and you don't need to be informed. That applies to all delegates (scrolling delegate methods of UIScrollView vs. code-scrolling, Table View manipulation, ...)
Anyway, what button index should the delegate be called with?.. there is no one when you dismiss programmatically
According to Why doesn't dismissWithClickedButtonIndex ever call clickedButtonAtIndex? the problem is that a different method is being called. However, that doesn't explain why you get erratic calls. On the devices I tested the dismiss method gets called correctly, so I only redirect it to the click version.
Maybe you should file a bug with Apple if you continue seeing the erratic behaviour.
There are alertView:clickedButtonAtIndex:, alertView:didDismissWithButtonIndex: and alertView:willDismissWithButtonIndex:. The method that you're referring to (clickedButtonAtIndex:) is only called when the user explicitly taps on a button on your alert view (hence 'clicked').
Programmatic calls via dismissWithClickedButtonIndex:animated: to dismiss the alert does not seem to call alertView:clickedButtonAtIndex:.
So, if you need some behavior to be always triggered upon the dismissal of the alert view—whether it was triggered by the user tapping on a button or triggered programmatically—then using the didDismissWithButtonIndex: and willDismissWithButtonIndex: makes more sense.

Xcode4, iOS: Certain parts of instance method being ignored, no errors, just passed by

new iOS guy here. I have a problem that Googling and searching on here has not shed any light on. I'm assuming this is basic. I have a simple app (app delegate and 1 view controller), and as part of it I'm using local notification. So, in the app delegate I use the 'didReceiveLocalNotification' to watch for the notifications. Depending on which one comes in, I then call one of several instance methods in my main view controller.
ie in the AppDelegate.m
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification {
MyViewController* controller = [[MyViewController alloc] autorelease];
if ([[notification.userInfo objectForKey:#"id"] isEqualToString:#"myKey"]) {
[controller checkActive];
}
}
Through logging and watching some breakpoints, this is all working. If the app is in the background, the notification comes in, app opens, and the correct instance method is called.
What I cannot figure out at all is why some parts of the instance method are simply being passed by, with no effect. For a simple example, if we have this:
-(void)checkActive {
ViewThing.alpha = 1.0;
NSLog(#"checkActive ran");
}
The log statement will show up fine, but the ViewThing will not change. Elsewhere in the main view controller I'm calling the same checkActive method with no problems and it changes the ViewThing. (via another interface button IBAction method in that case).
There are no errors, no warnings, the console shows nothing, putting a breakpoint on ViewThing shows that it hits the line. I'm stumped, cannot see what is different from trying to calling the method from the delegate vs. on an IBAction.
Thanks for any tips!
If the alpha is not correctly changing there a few possible issues with 1 and 2 being the most likely.
ViewThing is nil. Reasons could be is the view unloaded and you set it to nil or checkActive was called before the outlets were set.
ViewThing.alpha is being set on a thread that is not the main thread. Attempting to change UI elements on a separate thread will caused undefined behavior such as never updating the change or taking an extended amount of time to update the change. You can check if it is the main thread using [NSThread isMainThread].
ViewThing is pointing a different view.
1 & 2 can easily be checked by logging view
NSLog(#"checkActive ran %#", ViewThing);

How to call a method in another view when controller is dismissed

Basically the problem I am having is I am unable to call a method in my Main view controller(called Recorder) from another view called Table.
The table view is loaded as a Modalview controller(presentmodalViewController) from Recorder when the user clicks on a button. The table view lets my users change from setting 1 to setting 2 and has a done button (which calls dismissmodalviewcontroller) and returns the user to the main view(Recorder).
What I want is to call a method in Recorder when the done button is clicked on Table View. This method is called Changeview and changes the setting. I am currently unable to call this method properly.
The current code I have is:
changeView method
- (void)changeView
{
[levelsView changeView];
}
TableViewController interface file
RecorderViewController*recorderViewController;
#property (nonatomic, retain) RecorderViewController *recorderViewController;
TableViewController implementation file
#synthesize recorderViewController;
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[recorderViewController changeView];
}
Method called when Done button is pressed
- (IBAction) Switch: (id) sender {
[self dismissModalViewControllerAnimated:YES];
}
The current code does not give me any errors but it also does not change the setting. I have also tried to setup notifications with no luck. Any insight into this?
Are you setting the value of recorderViewController when you initialize the tableViewController (i.e., are you setting tableViewController.recorderViewController = self in your Recorder class)? If not, then your call to [recorderViewController changeView] is sending a message to nil – which doesn't crash, but it doesn't do anything either.
As an aside, passing your TableViewController a reference to your RecorderViewController is probably not the best way for two controllers to communicate: You may want to consider using NSNotificationCenter, or passing a model object and using Key-Value Observing.

Passing info to a delegate from a UIActionSheet

My understanding about passing data back to a delegate completely revolves around making a new view controller and having it conform to a protocol.
I am trying to get a time input back from a UIDatePicker set with the UIDatePickerModeCountDownTimer mode and I am running into issues.
Inside of Main1.m I create a UIActionSheet *action, and set everything up so that it presents itself with a UIDatePicker on a click. Inside of Main.m I also say:
main.delegate = self;
If this were not a UIActionSheet, I would make a protocol reference inside the new ViewController and then have the new vc pass data to a method that Main has, but I can't do that with a UIActionSheet!!
What am I missing? I assume there is something inherently different about Action Sheets, ut I can't figure it out.
Thanks for your help!!!
EDIT:
Sorry! I was giving a generic name to my view controller. It isn't actually Main.m, its FirstViewController.h/m
I didn't realize that my generic reference was getting mixed up with the Main.m file that is completely different than a vc.
I don't exactly understand why you're putting your delegate assignment in Main.m. I assume that you're setting up your UIActionSheet in a ViewController, and launching it from there. In this case, your ViewController is your delegate object. So you need to make sure that your ViewController implements the UIActionSheetDelegate. ie:
#interface SomeController : UIViewController <UIActionSheetDelegate>
Then you simply implement the required methods of that delegate in your view controller class, and that should do it. If I'm missing something about how you're implementing this, then you need to provide more code examples to examine.

Managing calls to objects that get deallocated when the view is backed out of

I have a view controller managed in a UINavigationController. My view controller puts up a "loading" screen, and uses ASIHTTP to asynchronously load table data. Then it renders its table, and in each cell there's an image that it uses ASIHTTP to asynchronously load. When it lands each image, it uses [self.tableView reloadRowsAtIndexPaths] to reload that row, inside which the image is fed to the UIImageView in each row.
Works great. But if I back out of the view before it's done loading, it crashes.
Where it crashes is in my -tableView:numberOfRowsInSection method, and NSZombies tells me it dies because it's asking for the -count of an NSArray called self.offers that has been deallocated.
That method looks like this:
-(NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
return [self.offers count];
}
Wrapping that return in if (self.offers) made no difference.
My -dealloc releases and sets-to-nil every one of these properties, including both self.offers and self.tableView itself. I even tried setting up a BOOL disappearing property, hitting it with YES in -viewWillDisappear, and hanging conditional behavior off that, but it doesn't work because viewWillDisappear doesn't seem to get called! Far as I can tell we're not getting ANY method called when the navigation bar pops us off.
What do I do about this?
EDIT:
Thanks to #cduhn (who's bucking for a check!), I did a bunch more looking at this. The problem has been, my -dealloc just isn't getting called when I pop this viewcontroller (nor my -viewWillDisappear nor -viewDidUnload or anything else I could use to unhook the delegation structure that's at the root of this problem).
Then I realized: THIS viewController isn't the one on the NavController stack! What's at the top of the stack right here is a shell view, just a segmented controller and a big empty UIView. I toggle the contents of that UIView between two other UIViewController subclasses depending on the state of my segmented controller. So when my view with the table on it's PARENT view gets popped from the nav stack, this CHILD I'm working on doesn't seem to get any notice about it. Which is odd, because I'm definitely releaseing it.
I can call its -dealloc from my shell controller. I could call its -viewWillDisappear too, for that matter. Is that how I should be handling this? Probably I should put something into my shell controller's viewWillDisappear like:
[[self.mainView subviews] makeObjectsPerformSelector:#selector(viewWillDisappear)];
...so that message propagates down to my child views.
Am I on the right track here, you think??
(Oh man... and that also explains why actions from inside this child table view can't get to self.navigationController! I've been puzzled about that for weeks!)
Bugs like this, where a method gets called on an object after it's been deallocated, often happen when a method gets called on a delegate after that delegate has been deallocated. The recommended practice to avoid bugs like these is to set any delegate (or delegate-like) properties of an object to nil before you release that object in the delegate's dealloc method. I know that's a confusing sentence, so I'll explain it in the context of your bug.
You have an asynchronous image download that finishes after you've backed out of your table view controller. When this happens, you're calling reloadRowsAtIndexPaths:withRowAnimation:, which results in a call to tableView:numberOfRowsInSection: on the table view's dataSource. This call is failing because that dataSource no longer exists.
The problem is that the table view object still has your controller set as its dataSource and delegate properties, even after your controller has been deallocated. The solution is to simply set these properties to nil in your controller's dealloc, like this:
- (void)dealloc {
self.tableView.dataSource = nil;
self.tableView.delegate = nil;
self.tableView = nil; // Releases as a side-effect due to #property (retain)
[super dealloc];
}
Now when your table view tries to call tableView:numberOfRowsInSection: on its dataSource, it will send the message to the nil object, which swallows all messages silently in Objective C.
You should also do the same thing with your ASIHTTPRequest's delegate, by the way.
Any time you have an asynchronous operation that calls delegate methods upon completion, it's particularly important that you set that delegate to nil. When using UITableViewController under normal circumstances you can typically get away without setting the dataSource and delegate to nil, but in this case it was necessary because you're calling methods on the tableView after its controller has gone away.
As far as I can tell, the user cannot actually "back out" of a view while the UITableView is loading it's data. The methods are not run on a thread and block the main one, also blocking UI interaction. I cannot replicate your results. Even, scrolling the table view quickly and then pressing the back button.
I suggest that the stack popping is not the problem here.