Why releasing an UIAlertView just afer showing it whereas it is not a blocking method? - iphone

I have been studying how to display a modal view with UIAlertView for a few hours and I understood that showing it does not "block" the code (the modal window is displayed and the program keeps running - we must use delegate to catch the selected actions on this modal window). Then I studied several examples and noticed that every example always release the modal window just after showing it. How can this work properly since the view will be released instantly as the code does not stop ?
Here is the example (there are many others on Google):
[[UIAlertView alloc] initWithTitle:#"Title" message:#"Message..." delegate:NULL cancelButtonTitle:#"OK" otherButtonTitles:NULL];
[alert showModal];
[alert release];
Thanks for your help,
Apple 92

I'm not sure where you're getting -showModal from (the usual method is just -show), but that act adds the alert to the view hierarchy. When a view is added as a subview of another view (I believe in this case it's a system-level view that is being added to) it's retained automatically, so you don't have to.

The alloc method will return you an instance that has a retain count of 1.
The showModal method probably retains the alert view so it remains on screen (and retained) until a button is tapped. It makes sense to me, since you are presenting it as a modal window, so it doesn't have a "parent", that is responsible of releasing it.

Related

Displaying UIAlertView from a non-view class

I'm looking at a friend's code and not sure how this works. There's a singleton that talks to a web service to download data. The WebServiceObject is a subclass of NSObject. The download comes from a click on the home page which is a UIViewController.
In the WebServiceObject, when something goes wrong, he does:
UIAlertView *alert = [[UIAlertView alloc] init...];
[alert show];
The alert shows. How does that alert show though since the code that executes it is a non-view object? The reason I'm asking is because I want to remove that alert when the network comes back on. Since the network observing is happening in the WebServiceObject, I'm not sure how to access the UIAlertView object. I thought in other code I've done, that I would do something like
[self.view viewWithTag:NETWORK_ALERT_TAG];
or something to that affect. Is this because the view code is in the model code and instead I should change his code to post a notification to the view code to display the alert instead and grab it that way? Thanks a bunch!
UIAlertView’s -show method creates its own window, overlaid on top of the app’s window, in which to display itself; as such, it isn’t part of the app’s view hierarchy. To hide it, you’ll need a reference to the alert view itself, maybe exposed as a property on your WebServiceObject.
It sounds like you want to have the UIAlertView without any buttons, I found this nice tutorial that presents a "Please Wait Alert"
http://mobiledevelopertips.com/user-interface/uialertview-without-buttons-please-wait-dialog.html
I think it will help you achieve what you want, if you don't want the user to be able to dismiss the alert

Keyboard loses hiding ability "if I use a UIAlertView"

I'm coding some iPad application, and a strange effect has started to appear.
I use a navigation bar so I can navigate freely through my views. Anyway, in one of these innerviews, I offer the possibility of checking if the user really wants to go back to the main view via an alert view (Just a Yes/no pair of buttons).
Once the user confirms he wants to go back, then I "manually" pop the view, and I navigate to the main view, where the keyboard hides.
BUT, the thing is virtual keyboard acts weirdly. Inside the "inner view", the keyboard is visible 90% of the time (It's normal, there are many UITextFields around). What do I do to hide it whenever I want it to become hidden? I put something like this in the "main viewWillAppear":
[self.view endEditing:YES];
Apparently it hides the keyboard, and when I do:
[[self navigationController] popToRootViewControllerAnimated:YES];
The iPad navigates back, and then the keyboard disappears. No big deal (the first time).
But then I decide to reenter the same view, so the keyboard appears, I do some fancy stuff. Then I decide to abort (with my button), I show the same alert view again, and I confirm I want to go back to the main view (just as before). The code is the same, but then, in the main view, the:
[self.view endEditing:YES];
line, does nothing, and what is worst, I've lost the ability to close the keyboard by code ANYWHERE inside my application, for the whole session.
Why do I know the UIAlertView has something to do? Because if I change the code inside my "Abort button" from this:
alertViewQuestionType=ALERT_ABANDON_TEST;
UIAlertView *alert = [[[UIAlertView alloc] initWithTitle:#"" message:NSLocalizedStringFromTable (#"STR_ABANDON_TEST_WARNING", #"ui_strings", #"") delegate:self cancelButtonTitle:NSLocalizedStringFromTable (#"STR_CANCEL", #"ui_strings", #"") otherButtonTitles:nil] autorelease];
[alert addButtonWithTitle:NSLocalizedStringFromTable (#"STR_ABANDON", #"ui_strings", #"")];
[alert show];
to this:
[[self navigationController] popToRootViewControllerAnimated:YES];
(that is, no confirmation for the user, the user just navigates back, whenever the abort button is pressed), then when the main view reaches the "viewWillAppear" function, the hide keyboard code, works. Not only THAT time, but ALL the time until the end of my app session.
I don't know if my question is 100% clear, but I'll be more than glad to add some extra pieces of code here if someone believes he can shed some light to this mistery...
Greetings!
I've finally solved my problem. I'm more than sure that I could have done better, but for now, even I still don't know "what causes the aforementioned behaviour", I'm more than happy.
The thing is that if I "popped back" a view, FROM inside the UIAlertView delegate code, iOS thinks it's something I shouldn't do, and its internal keyboard management code "becomes out of control". It's as if I'm popping back "too soon", or without having let iOS close whatever keyboard data structures it needed to close.
So a coworker (aleixventa) gave me a hint: "Why don't you delay a bit the 'popback' code, wrapping it inside a "NSTimer structure"?. And that's precisely what I did. My 'pop back' manual code, is now this:
NSTimer* testTimer = [NSTimer scheduledTimerWithTimeInterval:.05 target:self selector:#selector(popViewPorTimer) userInfo:nil repeats: NO];
Having a small function for my popViewPorTimer selector:
-(void) popViewPorTimer
{
[[self navigationController] popViewControllerAnimated:YES];
}
And now, no matter how many times the keyboard is dismissed via "Navigation pop back", it always becomes hidden. YAY!!
For extra bonus, if someone knows why all this happens, it will be a more than welcome piece of info.
I love when I "solve my questions" by myself :)
Well, I must have been completely misled, because I've removed all traces of [self.view endEditing:YES] and/or resignFirstResponder in ALL my .m files and I've discovered 2 things:
1.- They weren't doing ANYTHING. Keyboard auto disappears when needed.
2.- The bug I originally described, is still there.
So, as far as I know, when I just "press back" in the actual UINavigationBar "Back" button (in any inner view), the view, pops back, and if keyboard was present, it disappears. So far so good.
Then some other times, I need to simulate a "Back" press button. I do it with:
[[self navigationController] popViewControllerAnimated:YES];
It works flawlessly, that is, it just pops back, and if keyboard was present, it disappears.
BUUUT, if I write this "manual back" action INSIDE some UIAlertView Delegate I also need (where I deal with two Accept/Cancel buttons), this "manual back action" also pops back to the "parent" view (always, that's OK), but it hides the keyboard ONLY the first time it's called. Succesive calls to "manual back" via this accept/cancel UIAlertDelegate function, render the "keyboard hiding engine" completely unusable for the rest of the session.
I'm plain lost.

Dismissing an Alert View in different method than what it is in

I have an in app purchase in my app and when the purchase is "purchasing", or in progress, I have an alert view come up that says "Loading...". When the purchase was successful, restored, or failed, I'd like to call a method that releases the alert view. The only problem is, I can't try to release the alert view in another method because it will have no idea what alert view I am talking about and will produce an error. Now I have no idea if this is even the best way of trying to accomplish this, so all ideas are appreciated. Thanks!
case stateIsPurchasing { //or whatever it's called
UIAlertView *alert = [[UIAlertView alloc] message and delegate and button stuff here];
[alert show];
[alert release];
}
The UIAlertView is definitely not the right control to do this.
You should use the UIProgressView if you are able to display finite progression or UIActivityIndicatorView to show the "spinner".
I wouldn't use an alert view for this. Look for a progress HUD, such as SVProgressHUD, an excellent and beautiful loading view.
http://cocoacontrols.com/platforms/ios/controls/svprogresshud
SVProgressHUD functions as a singleton, so you can show/stop it from any class.
Declare a UIAlertView in your header as a retained property, synthesized, and released in dealloc. Store the alert view that you create in that code snippet using this pointer, and use the declared pointer in your other method. But don't call [alert release]; when you create the alert view (unless you like EXC_BAD_ACCESS errors!). Oh, if you're adding in-app purchases, watch out for Lodsys...

iPhone: UIAlert dialog Appears 3 Times for Every Call

I have a UIAlert that pops up 3 times every time it is called. It appears and then disappears before I can click on it. Could it be that the viewDidLoad itself is being called 3 times?
I implemented an UIAlert in the viewDidLoad of my app:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:alertMessage delegate:self cancelButtonTitle:ok otherButtonTitles:nil];
This is the viewDidLoad in the rootViewController, that manages the views:
- (void)viewDidLoad {
Kundenkarte *kartenAnsicht = [[Kundenkarte alloc]
initWithNibName:#"Kundenkarte" bundle:nil];
kartenAnsicht.rootViewController = self;
kartenAnsicht.viewDidLoad;
self.map = kartenAnsicht;
[self.view addSubview:kartenAnsicht.view];
[kartenAnsicht release];
// [super viewDidLoad];
}
The viewDidLoad that evokes the UIAlert is in the kartenAnsicht view controller.
I hope someone can help me because I'm out of ideas.
You don't need to call -viewDidLoad yourself, it's run automatically by the NIB-loading mechanism. That means you get extra invocations of -viewDidLoad: one by design, and extras whenever you call it.
First of all, you should never put any type of display in viewDidLoad. That method is intended for behind the scenes configuration after the view is first read from nib. There is no certainty that it will be called every time the view displays because after the first time it loads, the view maybe held in memory and not reloaded from nib.
Instead, put the call to evoke the NSAlert in viewWillDisplay or viewDidDisplay. This will display the alert each time the view appears.
I doubt that viewDidLoad is called three times but to check for that just put an NSLog in the method to see how many times it is called.
When you say that:
i implemented an NSAlert in the
viewDidLoad() of my app:
... what does that mean? What object exactly has the method? If it is the application delegate, this will not work because the application delegate protocol does not respond to viewDidLoad. It has to be in a UIViewController.
Edit01:
See this post that had the same problem: UIAlertView Pops Up Three Times per Call Instead of Just Once
Short answer: You kill the alert by releasing it. Either retain it as a property of the view controller or better yet, display the alert with runModal instead of show and capture the button number returned immediately.
It would be helpful to see the code around the alert call.
I am using an alert whenever the reachability changes. Since reachability is checked repeatedly, the alert could get called repeatedly. To alleviate that, I wrap the alert code like so:
if (!myAlert) { /* set up and show myAlert */ }
However, one problem with this is that when you click the Cancel button, the alert will remain non-nil, and therefore can never show again because of that conditional. If someone could add to this response with a fix for that, that would be great. I assume I can add a handler for the cancel button that will destroy myAlert.

Remove delegate on dealloc from unreferenced object

If learned the hard way that you should remove the delegate from an object if the life span of the delegate is shorter than the object. But how do you do this if you don't have a reference to the object anymore?
In my iPhone application I have a view controller vc which performs an asynchronous activity and is displayed as a modal view. A cancel button dismisses the modal view. If an error occurs, an UIAlertView alert is displayed. If the user taps on ok, both alert and the modal view disappear. Therefore vc is set
as delegate for alert and implements alertView:didDismissWithButtonIndex:. Something like this:
// UIViewController vc
...
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Error"
message:#"Something went wrong"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
self.alertView = alert; // needed to unset alertView.delegate in dealloc
[alert show];
[alert release];
...
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self dismissModalViewControllerAnimated:YES];
}
}
Normally, the alert view blocks all input. Unfortunately, it fails to do so in some edge cases.
If the user touches down on the cancel-button before the alert view appears and touches up after the alert view appears, the view is dismissed, but not the alert. vc gets deallocated and if the user then taps "ok" on the alert, the application crashes because a message was sent to the released object.
I solved this by assigning alert to a property of vc so I can set alert.delegate to nil in dealloc. I find this solution to be not very elegant, because I don't really need the reference to alert.
Is there a better approach?
Edit: Added the text in italics as clarification
Although, normally an alert view is presented over non changing content. So if the delegate is alive when the view appears it will likely be alive when it's dismissed. If that's not the case, you have to do exactly what you did, and unset the alert view's delegate manually if you no longer care about it's result.
So you do care about the alertview since you care about it's delegate method. The wrinkle is that the delegate may not apply by the time the alert is dismissed. So you need logic there, and for that logic you need to save a reference to the alert view in question.
In other words, you are doing it right. Although, it may have been helpful if UIAlertView retained it's delegate, but it doesn't seem like it does if it crashes when it's dismissed.
Lastly, I thought alert view blocked all screen input? If not, you can make it truly modal by setting vc.view.userInteractionEnabled = NO when the alert appears and switch it back when it's dismissed. This way the user can't dismiss the controller while the alert view is up. Which sounds a bit more sane to me.
From a UI perspective, when an alert view is present, it should demand the user's full attention. Perhaps you might consider disabling the Cancel button (as well as any other visible non-alert-view widgets) when there is an alert view present, and adding a button title to the UIAlertView instance which performs the same task. This would provide a clearer user interface and should also neatly solve your memory issue.
-(void)delayedDismiss {
[self dismissModalViewControllerAnimated:YES];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
[self performSelector:#selector(delayedDismiss) withObject:nil afterDelay:0.0];
}
If you no longer care about the results of the UIAlertView you should probably also dismiss it in the - (void) viewWillDisappear:(BOOL)animated