Calling UIAlertView delegate method crashed - ios

I have a viewController class A that have a method creating a UIAlertView and implement UIAlertView Delegate method, and a NSObject model class B for processing logging and networking. In class B, just allocating an A class instance, then call the method of A. The alert view was displayed normally, but when i clicked "Ok" button, it's just crashed. I want click "Ok" button to reopen keyboard let user continue login after failured. (Had declared UIAlertView protocol in header file.)
In viewcontroller class A:
- (void)displayAlertViewString:(NSString *)string
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Login Failured!"
message:string
delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Ok", nil];
[alert show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
if ([title isEqualToString:#"Ok"])
{
//reopen the keyboard let user continue login.
[self.passwordField becomeFirstResponder[;
}
And in model class B, i called display alert view method in the failure block of AFNetworking.
failure:^(AFHTTPRequestOperation *operation, NSError *error){
NSLog(#"%#", error);
RDLoginViewController *loginViewController = [[RDLoginViewController alloc] init];
[loginViewController displayAlertViewString:#"The entered email or password was incorrectly!"];
There is no any information in debugger, Xcode just stuck on the thread view. Can anyone help me figure it out? Thanks.

After the failure block execution, your loginViewController is released from memory, since nobody has a strong reference to it.
When the alertView tries to access it's delegate it crashes, since its delegate is not in the memory anymore.
I'd recommend you to take a look at the Advanced Memory Management Programming Guide.

Related

Is there a way to stagger when multiple Alert Views show up in the same View Controller?

So I have two different UIAlertViews in the same view controller and both alerts can be triggered at the same time. When both alerts are triggered, both alerts pop up at the same time, with the alerts being layered on top of each other. Is there a way to stagger the alerts so that when the first alert comes up, the second alert will not pop up until the user dismisses the first alert? For my code, this is the format I'm using
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"ERROR!"
message:#"Error message here!"
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
try the following:
create two properties
#property (weak, nonatomic) UIAlertView *visibleAlertView;
#property (strong, nonatomic) UIAlertView *pendingAlertView;
every time when you want to present an alertview from your code make a check
UIAlertView *newAlertView = [[UIAlertView alloc] init...
if (self.visibleAlertView) {
self.pendingAlertView = newAlertView;
} else {
self.visibleAlertView = newAlertView;
[newAlertView show];
}
and finally:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (self.pendingAlertView) {
UIAlertView *newAlertView = self.pendingAlertView;
self.pendingAlertView = nil;
self.visibleAlertView = newAlertView;
[newAlertView show];
}
}
hope that helps :)
EDIT
you could even stack the pending alertviews:
#property (strong, nonatomic) NSMutableArray *pendingAlertViews;
...
self.pendingAlertViews = [NSMutableArray array];
before presenting an alertview:
UIAlertView *newAlertView = [[UIAlertView alloc] initWithTitle:#"Title" message:#"Message" delegate:self cancelButtonTitle:#"Ok" otherButtonTitles:nil];
if (self.visibleAlertView) {
[self.pendingAlertViews addObject:newAlertView];
} else {
self.visibleAlertView = newAlertView;
[newAlertView show];
}
and in dismiss:
if (self.pendingAlertViews.count > 0) {
UIAlertView *av = self.pendingAlertViews.firstObject;
[self.pendingAlertViews removeObjectAtIndex:0];
self.visibleAlertView = av;
[av show];
}
hope it helps :)
Why don't you make a class level variable that indicates an alertView is open. Then before you open one you check that variable and if it's set you don't pop up the second one. Instead you could have it set another variable that indicates the second box should pop up. Then in the - alertView:clickedButtonAtIndex: method you can pop up the second one if the second variable is set.
I think I am pretty late for this but still posting as It might be useful for someone looking for this.
I have created a AQAlertAction subclass for UIAlertAction. You can use it for staggering Alerts, the usage is same as you are using UIAlertAction. All you need to do is import AQMutiAlertFramework in your project or you can include class also (Please refer Sample project for that). Internally It uses binary semaphore for staggering the Alerts until user handle action associated with current alert displayed. Let me know if it works for you.

UIAlertView crashes when button tapped: how to solve?

I have a class that I instance to show an alert view like this:
- (void)showAlert
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Do you want to try again?"
message:nil
delegate:self
cancelButtonTitle:#"Yes"
otherButtonTitles:#"No", nil];
[alertView show];
}
}
I need self to be the delegate because I need alertView:didDismissWithButtonIndex: to be called to perform some actions when the user taps the alert view's button. This usually works well, but from time to time, I get this crash:
SIGSEGV
UIKit-[UIAlertView(Private) modalItem:shouldDismissForButtonAtIndex:]
I guess this is because the delegate, for any reason, was released, right? Or is this because what was released was the alert view? How could I solve this? I need the alert view to have a delegate, and I've reading several related posts and I couldn't find an answer that fits my scenario.
I'm testing in iOS 7.0, I don`t know if that could have to do with the issue.
Thanks in advance
It seems that you tap alert when its delegate is released:
delegate:self
It happens because UIAlertView delegate property is of assign type (not weak!).
So your delegate potentially can point to released object.
Solution:
in dealloc method you need to clear delegate for your alertView
- (void)dealloc
{
_alertView.delegate = nil;
}
But before you need to make iVar _alertView and use it for your alertViews
- (void)showAlert
{
_alertView = ...;
[_alertView show];
}
Update your code as follows:
- (void)showAlert {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Do you want to try again?"
message:nil
delegate:self
cancelButtonTitle:#"Yes"
otherButtonTitles:#"No", nil];
[alertView show];
}
Its due to you are missing the nil for otherButtonTitles part.
Missing sentinel in method dispatch warning will be shown if you didn't add nil.
That's because the alertView's delegate object released while clicking the button. I think it's a bug of SDK:
#property(nonatomic,assign) id /*<UIAlertViewDelegate>*/ delegate; // weak reference
should be:
#property(nonatomic, weak) id /*<UIAlertViewDelegate>*/ delegate; // weak reference
To fix the issue:
add a weak delegate for UIAlertView using association.
swizzle the init, setDelegate: delegate methods, set alertView delegate to self, set step 1 weak delegate with the param delegate.
implement all delegate methods, deliver the methods using the weak delegate.

UIAlertView dismissal in utility class

In my application i want alertview in many views.So what i did is just wrote a single alertview in a utility class and use it everywhere.This is working fine.
I even tried by setting <UIAlertViewDelegate> but in vain.
Utility Class
#interface SSUtility: NSObject<UIAlertViewDelegate> {
}
+(void)showAllert;
#end
#implementation SSUtility
+(void)showAllert{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"gotoappAppstore",#"") message:#"" delegate:self cancelButtonTitle:NSLocalizedString(#"Ok",#"") otherButtonTitles:nil];
[alert show];
[alert release];
}
#end
Now from my view
-(void)pressButton{
[SSutility showAllert]
}
Now i want to give a button action for alert view click and call a method on that button action.
So im stuck with,in which class i want to implement this method.I tried it in utility class and viewc controller but the method is not getting triggered when "ok" button is pressed.
-(void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
Can anyone please help me on this?
Thanks in advance.
You wire the alert view button response method by setting your alert view object delegate usually to the owner object and implementing the – alertView:clickedButtonAtIndex: method.
You need 4 parts in your code:
instantiate your UIAlertView object
send show message to your UIAlertView object
set delegate
implement the delegate method
Example:
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"myTitle" message:#"myMessage" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitle:#"Another button"];
[myAlertView setDelegate:self];
[myAlertView show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 0) //index 0 is cancel, I believe
{
// code for handling cancel tap in your alert view
}
else if (buttonIndex == 1)
{
// code for handling button with index 1
}
}
I would recommend you get more familiar with how delegates work. This'll come back again a lot.
You set delegate:nil in your UIAlertView's init.
You should set to delegate:self, like this:
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(#"gotoappAppstore",#"") message:#"" delegate:self cancelButtonTitle:NSLocalizedString(#"Ok",#"") otherButtonTitles:nil];
in order to use the delegate in the same class (a.k.a. self).
As a sidenote, if you use Automatic Reference Counting (ARC), you do not need [alert release] (your Xcode compiler should warn you about this)

Dismiss UIAlertView without making it a property

I have a UIAlertView that I display with no buttons. I'd like to programmatically dismiss it after some action is processed (it is a "Please wait" alert dialog). I'd like to dismiss it, though, without the need for the UIAlertView to be a property.
Why: Now I am allocating the alert view to a #property - e.g.: I am creating a class variable. I don't feel like it deserves to have it that way - because frankly, it is displayed only when the View Controller is loading. I thought it is somehow added as a subview, that I could pop it from the stack when the loading is done, but that didn't work.
What: I create the alert dialog (no buttons) and show it. Then I start processing data - syncing with server. It only happens once and it is not a frequent thing. However, other object takes care of the sync and is implemented as observer pattern - the object itself reports, when the data has been loaded. That's when I dismiss the dialog. I just wanted to avoid using #property for the dialog.
This is how I do it (simplified):
#property (nonatomic, strong) UIAlertView *av;
- (void)setup {
...
[self.av show];
[self loadData];
}
- (void)loadData {
...loading data...
[self.av dismissWithClickedButtonIndex:0 animated:YES];
}
Is there a way how to dismiss it without the need for "storing" it to #property?
Blocks retain variables they capture. You can take advantage of that behaviour, but you should understand what you're doing there:
UIAlertView *av = [[UIAlertView alloc] initWithTitle:#"Title"
message:#"Message"
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:nil];
[av show];
dispatch_async(dispatch_queue_create("com.mycompany.myqueue", 0), ^{
sleep(5);
dispatch_async(dispatch_get_main_queue(), ^{
[av dismissWithClickedButtonIndex:0 animated:YES];
});
});
The sleep(5) is just simulating your long running task.
Instead of using UIAlertView, I'd consider using a library like this: https://github.com/jdg/MBProgressHUD

UIAlertView Shows two times

Hope you are all doing good.
Where is my problem is concern one UIAlertView shows twice while executing the code. I have written it within -(void)viewDidAppear:(BOOL)animated as shown following
-(void)viewDidAppear:(BOOL)animated
{
//Finding city Name from coordinate
UIAlertView * alertForCoordinate=[[UIAlertView alloc]initWithTitle:#"Coordinate" message:[NSString stringWithFormat:#"latitude==>%f\nlongitude==>%f",self.userLocation.coordinate.latitude,self.userLocation.coordinate.longitude] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil,nil];
[alertForCoordinate show];
[SVGeocoder reverseGeocode:CLLocationCoordinate2DMake(self.userLocation.coordinate.latitude, self.userLocation.coordinate.longitude)
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (placemarks.count>0)
{
CLPlacemark * placemark=[placemarks objectAtIndex:0];
currentLocationUpcoming =[placemark locality];
UIAlertView * alert=[[UIAlertView alloc]initWithTitle:#"Location" message:[NSString stringWithFormat:#"currentLocation==>%#",currentLocationUpcoming] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[self.eventsTableView reloadData];
}
}];
}
As view appears First UIAlertView gets call and dismiss automatically then second gets call When I tap OK button of second alert and again first UIAlertView appears . I am struggling with it since last 4 hours and some of the answer put here also not intended the context in which I am working. Can anybody suggest me where I am wrong.
Thanks
You don't actually have the first UIAlertView showing twice - the animation just makes it look like this because you are showing two UIAlertViews in a row. You're probably seeing Alert 1-> Alert 2-> tap OK on Alert 2 -> Alert 1. This is because the UIAlertViews stack up if multiple are shown.
Think of it this way: Alert 1 shows first but animates for only a very brief period until it is interrupted by Alert 2 showing. Dismissing Alert 2 animates Alert 1 onscreen again, and even though it is still the same alert, it looks like it is showing twice.
Try this simple test case:
-(void)viewDidAppear:(BOOL)animated
{
[[[UIAlertView alloc] initWithTitle:#"Alert #1" message: delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
[[[UIAlertView alloc] initWithTitle:#"Alert #2" message: delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
}
You will see "Alert #1" for a fraction of a second, then "Alert #2" will remain onscreen. Tapping OK on "Alert #2" will show "Alert #1" again because it was below Alert #2 on the stack.
Your second alert overlaping the first one, and after dismissing second alert, the first one reappears on screen.
-(void)viewDidAppear:(BOOL)animated
{
UIAlertView * alertForCoordinate=[[UIAlertView alloc]initWithTitle:#"Coordinate" message:[NSString stringWithFormat:#"latitude==>%f\nlongitude==>%f",self.userLocation.coordinate.latitude,self.userLocation.coordinate.longitude] delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil,nil];
alertForCoordinate.tag = 1;
[alertForCoordinate show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 0 && alertView.tag == 1) {
[SVGeocoder reverseGeocode:CLLocationCoordinate2DMake(self.userLocation.coordinate.latitude, self.userLocation.coordinate.longitude)
completion:^(NSArray *placemarks, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (placemarks.count>0)
{
CLPlacemark * placemark=[placemarks objectAtIndex:0];
currentLocationUpcoming =[placemark locality];
/*****************Second Alert******************/
UIAlertView * alert=[[UIAlertView alloc]initWithTitle:#"Location" message:[NSString stringWithFormat:#"currentLocation==>%#",currentLocationUpcoming] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
[self.eventsTableView reloadData];
}
}];
}
}
i had same issue "UIAlertView Shows two times", but in my case it was because of coding error with long press gesture recognizer (different than original question, but may be useful for other folks that google uialertview showing twice). inside method to handle long press, i called method to show UIAlertView. my mistake was not recalling that handle long press method gets called twice each time user long presses (once for press and hold down, once when user releases their finger). the correct code recognizes gesture state (code below).
-(IBAction)handleHoldOnContact:(UILongPressGestureRecognizer*)sender {
if (sender.state == UIGestureRecognizerStateBegan){
// NSLog(#"UIGestureRecognizerStateBegan hold");
//Do Whatever You want on Began of Gesture
// ...
} else if (sender.state == UIGestureRecognizerStateEnded) {
// NSLog(#"UIGestureRecognizerStateEnded release");
//Do Whatever You want on End of Gesture
// ...
}
}
I too get the same issue. To overcome this problem, i done the following things. Get the alertview as global variable. And then,
if(!alertForCoordinate){
alertForCoordinate=[[UIAlertView alloc]initWithTitle:#"Coordinate" message:[NSString stringWithFormat:#"latitude==>%f\nlongitude==>%f",self.userLocation.coordinate.latitude,self.userLocation.coordinate.longitude] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil,nil];
[alertForCoordinate show];
}
After that, when you clicking "OK" button from the Alertview. set that to nil. Like the following,
alertForCoordinate = nil;
Hope, this will help you.
It could be a case of duplicating a button, and therefore it's IBaction references, in main.storyboard.
I had a button that was styled perfectly in main.storyboard, so I alt drag duplicated it and attached a new IBaction to the viewController. I didn't realize it but when I tapped that new button it fired the IBaction for both the first button and the second button, because it was hooked up to both, as it was a duplicate of the first.
I right clicked the second button in main.storyboard, saw the reference to the IBaction from the first button AND the second, then deleted the reference to the first. Problem solved. What a nightmare. Hope this helps someone.

Resources