alertView:clickedButtonAtIndex: causes program closing - objective-c

I have this piece of code which allocates 2 UIAlertView :
promptConnexion = [[UIAlertView alloc] initWithTitle:#"Connexion"
message:#"\n\n\n"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
promptConnexion.tag = 1;
promptInscription = [[UIAlertView alloc] initWithTitle:#"Inscription"
message:#"\n\n\n\n\n"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
promptInscription.tag = 2;
and this one which specifies what to do when buttons are pressed :
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 1)
{
if (buttonIndex == 1)
{
NSLog(#"Connexion button OK");
}
}
if (alertView.tag == 2)
{
if (buttonIndex == 1)
{
NSLog(#"Inscription button OK");
}
}
}
In my console I actually get "Connexion button OK" and "Inscription button OK" when I should to.
But right after my program is closing unexpectedly whithout any warning or error in my console.
Moreover, if I don't overload alertView:clickedButtonAtIndex: my UIAlertView is just closing, my app still runs correctly, but my buttons do nothing else (obviously).
I also wrote this code :
and showed my 2 UIAlertView this way :
[promptConnexion show];
[promptConnexion release];
and :
[promptInscription show];
[promptInscription release];
and overloaded alertView:didDismissWithButtonIndex: too :
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
[alertView release];
}
What am I doing wrong?

Here is your problem;
[alertView release];
You don't want to be doing that at all. In fact if that is all you are doing inside the method, you can remove the method completely as well. This is causing a double release of the alert view which you have already released using [promptConnexion release] etc.
One way to detect such things is to run your program with NSZombiesEnabled as that can show you when you try to release an object that is already released.

This code is your problem:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex: (NSInteger)buttonIndex
{
[alertView release]; // Should not be released here!
}
The reason is, you have already released the alertView here:
[promptConnexion release];
[promptInscription release];
You have done your "duty" by releasing the object you have allocated. The alert view object is now under the responsibility of the operating system displaying that alert view, and it will release it when necessary, without any action of your part.
Just remove that code and it should work.
Also, you should read Apple's Memory Management Rules to have a better understanding of this issue

[promptInscription release]; and [promptConnexion release]; already releases the alertViews. You are releasing it and then after accessing the tag for those alert views and then release it again which I think is the reason for crash.

you should not release the alertView in
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
you already release the alertView instances so there is no need to release that..

Try with not release your alertView in below function.
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex`
SO your function should be
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
//Put you code here
}

Just do one thing write your code as below
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex
{
if (alertView.tag == 1)
{
if (buttonIndex == 1)
{
NSLog(#"Connexion button OK");
}
}
if (alertView.tag == 2)
{
if (buttonIndex == 1)
{
NSLog(#"Inscription button OK");
}
}
}
No need to override clickedButton Index method just remove it

Related

Why is the UIAlertView not blocking until user responds by tapping a button?

In my iOS 7 app, I need to verify the user wants to deleta a selected record from Cord Data. I have the UIAlertViewDelegate defined in the .h file. This is the code to display the alert:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Are you sure you want to delete this record?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Delete", nil];
[alert show];
if(alertButtonTapped == 0)
return;
// remainder of code to delete record follows (was omitted)
This is the code to check which button was tapped:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
alertButtonTapped = [NSNumber numberWithInteger:buttonIndex];
return;
}
The problem is the alert is displayed and then immediately falls through the remainder of the code in that method. I have never seen this before; usually it blocks until the user has responded by tapping one of the buttons (at least I thought it did). What do I need to do to make this alert block until the user responds? (I was looking at UIAlertView blocks, but not sure that would do the job since it appears to use a different thread)
This is how UIAlertView works -- it doesn't block, thus why it has the UIAlertViewDelegate methods for actually implementing a response.
If the "remainder of code" is what should happen after they tap a button (e.g. the "Delete" button), then move all of that code into the delegate method, like:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex != alertView.cancelButtonIndex) {
// code to delete record
}
}
EDIT - adding example to answer a comment
So if you have multiple UIAlertViews in the same class, you could differentiate between them using the tag attribute of UIView (UIAlertView is-a UIView). So it could be something like this:
const NSInteger kDeleteAlertTag = 100; // declared at the top of your .m file, perhaps.
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Warning"
message:#"Are you sure you want to delete this record?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"Delete", nil];
alert.tag = kDeleteAlertTag;
[alert show];
Then your delegate response might look like this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView.tag == kDeleteAlertTag) {
if (buttonIndex != alertView.cancelButtonIndex) {
// code to delete record
}
}
else if (alertView.tag = kDoSomethingElseAlertTag) {
DoSomethingElse();
}
}

confirmation before open in safari

Im trying to add a UIAlertView to warn the user that the link will open in Safari. The user can then choose OK(open the url) or cancel which should just close the alert and return to the links.
I have three different UIButtons which has 3 different URLs.
Right now ive added a IBAction to all buttons and all buttons has Tags (which i think i can use somehow :D). I guess - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex: delegate will be good to use to..
My question: how should the UIAlertView know what URL to open i user clicks ok?
I suggest that you use a subclass of UIAlertView that is able to track some more properties along. I do this in all my projects and it is much simpler.
One solution to do this would be to subclass UIAlertView to MyAlertView and add a #property(nonatomic, retain) id userInfo; or #property(nonatomic, retain) NSURL* urlToOpen. Thus you can attach custom data to your UIAlertView and retrieve it in the delegate method to do whatever you need with it.
Another solution, and my preferred one actually, is to add Objective-C blocks support to UIAlertView, to be able to use UIAlertView using the blocks API instead of using a delegate. This is particularly useful if you use multiple UIAlertViews in the same class and with the same delegate, as using a single delegate to handle the different instances is a mess.
I personally use this technique all the time, as it also makes my code more readable by having the code that executes when the button is tapped right next to the code that shows the alert, instead of having it at a complete different places when you use delegate methods.
You can look at my OHAlertView subclass here on GitHub that implement this already. The usage is really simple and allow you to use blocks for each alert view instead of a common delegate, see below.
Usage Example
-(void)confirmOpenURL:(NSURL*)url
{
NSString* message = [NSString string WithFormat:#"Open %# in Safari?",
url.absoluteString];
[OHAlertView showAlertWithTitle:#"Open URL"
message:message
cancelButton:#"No"
okButton:#"Yes"
onButtonTapped:^(OHAlertView* alert, NSInteger buttonIndex)
{
if (buttonIndex != alert.cancelButtonIndex)
{
// If user tapped "Yes" and not "No"
[[UIApplication sharedApplication] openURL:url];
}
}];
}
Then each button can have its own action:
-(IBAction)button1Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.google.com"]];
}
-(IBAction)button2Action
{
[self confirmOpenURL:[NSURL URLWithString:#"http://www.stackoverflow.com"]];
}
Or you can have a common IBAction for all your buttons opening URLs:
-(IBAction)commonButtonAction:(UIButton*)sender
{
NSUInteger tag = sender.tag;
NSString* urls[] = { #"http://www.google.com", #"http://www.stackoverflow.com" };
NSURL* buttonURL = [NSURL URLWithString: urls[tag] ]; // in practice you should check that tag is between 0 and the number of urls to be sure, that's just an example here
[self confirmOpenURL:buttonURL];
}
Solved it like this:
added a tag when creating the UIAlertView. Like this:
- (IBAction)PressedButton {
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Link"
message:#"Want to open in safari?"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
message.tag = 2; //different tag for each button
[message addButtonWithTitle:#"Cancel"];
[message show];
}
Then when - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex delegate was thrown I did this:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == alertView.cancelButtonIndex){
if (alertView.tag == 1)
{
//go to URL1
}
else if (alertView.tag == 2)
{
//go to URL2
}
else if (alertView.tag == 3)
{
//go to URL3
}
}
}
Your button action method should have a signature like this:
-(void)doSomething:(id)sender;
whereby sender will be the button. Based on this you could find out which URL is meant.

open a uiviewcontroller from uialertview button

I want to open a UIViewController if a UIALertView Button is pressed.
I have the code for that. However, the uiviewcontroller is not being opened :(
I am sure the uialertview is working fine and all. and the code for the uiviewcontroller is fine as well. (it works when called from other places).
Any help ?
Thanks.
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) //Yes, Sign Me Up!
{
NSLog(#"0");
ViewerWebController *web = [[ViewerWebController alloc] initWithURL:[NSURL URLWithString:#"http://www.funimation.com/"]];
[web setShowToolbar:YES];
[self.navigationController pushViewController:web animated:YES];
[web release];
}
else if (buttonIndex == 1) //Remove from List
{
NSLog(#"1");
[[NSUserDefaults standardUserDefaults] setBool:false forKey:#"subscribeButtonOption"];
[_tableView reloadData];
}
else if (buttonIndex == 2) //"Maybe Later"
{
NSLog(#"2");
}
}
The alert view doesn't have a navigation controller. You would need to keep a reference to the navigation controller you want to push the view controller on to.
This may work..
- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// the user clicked one of the OK/Cancel buttons
if (buttonIndex == 0)
{
YourRootNavigationController *navController = [YourRootNavigationController sharedInstance]; // singleton
[navController pushViewController:YOUR_CONTROLLER animated:YES];
}
else
{
NSLog(#"cancel");
}
}
Use didDismissWithButtonIndex rather clickedButtonAtIndex.
The former is invoked after the UIAlertView has been removed from the screen hierarchy, while the latter occurs while the UIAlertView is still on screen. While on screen, UIAlertView alters the nature of the app; that means bad things happen when trying to push views to the nav controller.
This misuse of clickedButtonAtIndex seems to be common misinformation floating around the internet and StackOverflow. Cost me hours of frustration.

Open AlertView before calling method

I'd like to call a method
[self someMethod:bar]
But how can I open an AlertView before? someMethoud should wait for AlterView OK-Button. Do i need to write a complex task manager? Or is there a smart solution?
display the alert, and when the user clicks the button, then call your method.
remember to add the delegate to your class
#interface YourViewController : UIViewController <UIAlertViewDelegate>
code in your controller
// display the alert
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Alert" message:#"Message"
delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
[alert show];
[alert release];
}
// UIAlertViewDelegate method - get the click here and take action
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0) {
// ok button pushed
[self someMethod:bar];
}
}
You would need to implement the delegate method didDismissWithButtonIndex here http://developer.apple.com/library/ios/ipad/#documentation/UIKit/Reference/UIAlertViewDelegate_Protocol/UIAlertViewDelegate/UIAlertViewDelegate.html
The, depending on the button that was tapped, you would call your method.

wait_fences: failed to receive reply: 10004003 - what?

Been getting this odd error.
heres the deal - in the below method i have an alert view come up, take a U/N and PW, then atempt to start another method.
The method
-postTweet
does not get activated
I just get this error in console
wait_fences: failed to receive reply: 10004003
Which is really odd - as ive never seen it before
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (alertView == completeAlert ) {
if (buttonIndex ==1) {
passPromtAlert = [[UIAlertView alloc] initWithTitle:#"Enter Name" message:#"Please enter your Username and password - they will be saved\n" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"Tweet", nil];
[passPromtAlert addTextFieldWithValue:#"" label:#"Enter username"];
[passPromtAlert addTextFieldWithValue:#"" label:#"Enter password"];
textField = [passPromtAlert textFieldAtIndex:0];
textField2 = [passPromtAlert textFieldAtIndex:1];
textField.clearButtonMode = UITextFieldViewModeWhileEditing;
textField.keyboardType = UIKeyboardTypeAlphabet;
textField.keyboardAppearance = UIKeyboardAppearanceAlert;
textField.autocapitalizationType = UITextAutocapitalizationTypeWords;
textField.autocorrectionType = UITextAutocapitalizationTypeNone;
textField.textAlignment = UITextAlignmentCenter;
textField2.secureTextEntry = YES;
textField2.clearButtonMode = UITextFieldViewModeWhileEditing;
textField2.keyboardType = UIKeyboardTypeAlphabet;
textField2.keyboardAppearance = UIKeyboardAppearanceAlert;
textField2.autocapitalizationType = UITextAutocapitalizationTypeWords;
textField2.autocorrectionType = UITextAutocapitalizationTypeNone;
textField2.textAlignment = UITextAlignmentCenter;
[passPromtAlert show];
}
}
if (alertView == passPromtAlert ) {
if (buttonIndex == 1) {
NSLog(#"here");
[self postTweet];
}
}
}
Any help would be appreciated
Thanks
Sam
ADDED:
If you need to see more code, then let me know
Here I was also facing the same issue in my project and I managed to solve the issue now. You need to call the method using NSTimer.
So, Here you are calling one alert inside another alert means when one alert dismiss then the new alert you are calling there. There is no problem in that. You can do it easily. Instead of defining alertview inside the method "- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (alertView == completeAlert )", You can create one method e.g. "Show_AlertMessage" and call it using timer like this.
[NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:#selector(Show_AlertMessage) userInfo:nil repeats:NO];
Let me know if you can get rid of this issue. We will solve this issue as I did for my side.
I believe the problem is caused by you creating another alert view before you dismiss the first one. Since only one alert view is supposed to be present at anyone one time the second alert view is stepping on the first one.
You need to dismiss the first view before creating and showing the second.
Solution is here! I had same error, now i got the solution, this may help you. Use this delegate of AlertView
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{
Your UITextField* isn't resigning its firstresponder status before the alert is dismissed.
This messes up the responder chain. Add [textField resignFirstResponder] or [textField2 resignFirstResponder] as applicable in the UIAlertViewDelegate implementation (alertView:clickedButtonAtIndex:)
Also, schedule your second UIAlertView to show after the first is dismissed (you can do this using (performSelector:withObject:afterDelay:)