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.
Related
I am working on XCode7 and on this project I am working on, UIAlertview is working fine.
I set up a cocoa-touch file, defined a new class inheriting from NSobject,
NSVerifier.h
#interface NSVerifier : NSObject<UITextFieldDelegate, UIAlertViewDelegate>
{
..
}
and inside the implementation I have:
NSVerifier.m
- (void)alertView:(UIAlertView *)aView clickedButtonAtIndex:(NSInteger)buttonIndex{
//Breakpoint here
..
}
-(void)show{
UIAlertView* t = [[UIAlertView alloc] initWithTitle:#"test" message:#"testing" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"ok", nil];
[t show];
}
inside viewDidAppear:
NSSVerifier* iVerifier = [[NSVerifier alloc] init];
[iVerifier show];
I was trying all day to make that breakpoint stop when I press the ok button, but the breakpoint was never reached.
On the other hand...
I have another project that was made during iOS7 times, and I imported this class in. It works there...
When I put the delegation methods I got a warning the UIAlertView is deprecated in iOS9, and on which device I am debugging (iOS9), But since the deployment target on both devices is iOS7, should this not work still?
Have you set the alert delegate to be self:
t.delegate = self;
Try that?
Update: And make sure where your NSVerifier is created it has a strong reference so the UIAlertView delegate doesn't get dropped later once the UIAlertView is shown. E.g in your ViewController create iVerifier in your .h:
#property (nonatomic, strong) NSVerifier *iVerifier;
Just another thought
If you don't retain iVerifier and it gets deallocated causing UIAlertView's delegate property to be nil.
Following should help:
// In interface
#property (strong, nonatomic) NSSVerifier* iVerifier;
// In implementation
NSSVerifier* iVerifier = [[NSVerifier alloc] init];
self.iVerifier = iVerifier;
[iVefirier show];
I think iVerifier is released after the code go out viewDidAppear.
Please try: handle the iVerifier by a property with strong reference.
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.
I'm creating a wrapper for UIAlertView (I know about UIAlertController and about several already existing wrappers, it's also for educational purposes).
Suppose it looks like this (very shortened version):
#interface MYAlertView : NSObject
-(void)show;
#end
#interface MYAlertView()<UIAlertViewDelegate>
#end
#implementation MYAlertView
-(void)show {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Some title"
message:#"Some message"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil];
[alertView show]
}
#pragma mark UIAlertViewDelegate implementation
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
//Do something.
}
#end
And, for instance, I use it like this:
// USAGE (inside some ViewController)
-(void)showMyAlert {
dispatch_async(dispatch_get_main_queue(), ^{
MYAlertView *myAlertView = [[MYAlertView alloc] init];
[myAlertView show];
});
}
The problem I have is the following:
[myAlertView show] causes the alertView to appear. myAlertView is set as a delegate of the alertView.
There is the only strong reference to myAlertView: inside the block in the showMyAlert method. When it's finished, myAlertView is deallocated.
When the user clicks a button on the alertView, the alertView calls it's delegate method, but the delegate (myAlertView) is deallocated already, so it causes BAD_ACCESS (the delegate in UIAlertView is declared as assign, not weak).
I want to make MYAlertView as easy to use as it is with UIAlertView, so I don't want to make the user store a strong reference to it somewhere (it is inconvenient).
So, I have to keep the myAlertView alive as long as the alertView is shown somehow. The problem is I can't think of any way other than creating a strong reference inside MyAlertView, assigning it to self, when I show the alertView, and assigning it to nil, when I dismiss it.
Like so (only the changed bits):
#interface MYAlertView()<UIAlertViewDelegate>
//ADDED:
#property (nonatomic, strong) id strongSelfReference;
#end
#implementation MYAlertView
-(void)show {
UIAlertView *alertView = [[UIAlertView alloc] init /*shortened*/];
[alertView show]
//ADDED:
self.strongSelfReference = self;
}
#pragma mark UIAlertViewDelegate implementation
//ADDED:
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
self.strongSelfReference = nil;
}
#end
It should work: the moment the alertView is dismissed, the strongSelfReference will be set to nil, there will be no strong references left to the myAlertView, and it will get deallocated (in theory).
But keeping a strong reference to self like this looks evil to me. Is there a better way?
UPDATE: The MYAlertView in reality is an abstraction layer around the now deprecated UIAlertView and a new UIAlertController (iOS 8+), so subclassing UIAlertView is not an option.
Yes, your object should keep a strong reference to itself. It's not evil to do so.
A self-reference (or, in general, any reference cycle) is not inherently evil. The evil comes in creating one unintentionally such that it is never broken, and thus leaks objects. You're not doing that.
I feel like the answer here is to actually implement MYAlertView as a subclass of UIAlertView instead of an object that floats in the ether. It will stick around as long as your internal UIAlertView would have regularly stuck around.
#interface MYAlertView : UIAlertView
#end
#implementation MYAlertView
- (instancetype)init {
if (self = [super initWithTitle:#"Some title"
message:#"Some message"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:nil]) {
// Other setup?
}
return self;
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
// Respond.
}
#end
Update: You should instead create an iOS7 analogy for UIAlertViewController.
#interface MyAlertViewController : UIViewController <UIAlertViewDelegate>
+ (id)makeMeOne;
#end
#implementation MyAlertViewController
- (void)viewDidAppear:(BOOL)animated {
UIAlertView *alert = [[UIAlertView alloc] init..];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
// Respond.
[self.navigationController popViewControllerAnimated:NO];
}
+ (id)makeMeOne {
if (iOS7) {
return [[self alloc] init];
} else {
return [[UIAlertViewController alloc] init];
}
}
#end
Fill in the blanks for setup.
In my opinion, this is an indicator of a bad design.
If you are creating a wrapper for both iOS version's you would be better off exposing a delegate for MYAlertView that mirrors the relevant dismiss actions (or else you won't be able to act on the callbacks and further than this wrapper).
If you are keeping a strong reference to yourself without adding any actual value to the class you are wrapping, maybe it would be better to write a wrapper that accepts a block to callback on completion? At least this way you can monitor the user's actions and let the caller dictate how long the alert is relevant.
After all, if you pass on the delegation then the problem is solved in a readable manner?
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)
I have been googling this problem for almost a whole day now, without getting any closer to a solution, so i would like to ask you guys.. :)
I'm working on an iOS app, which should connect to a mbed over WiFi and give the user a dialog if it connects and if it doesn't and if not, then give the user the possibility to retry.
My problem is now that i have implemented the connecting method in appdelegate.m and it is from here I would like to show the alerts..
The alerts it self works fine, but I have problems detecting when a button is pressed, the clickedButtonAtIndex is not being called.
I have added the UIAlertViewDelegate in the appdelegate.h, like so:
#interface AppDelegate : NSObject <UIApplicationDelegate, UITabBarControllerDelegate, UIAlertViewDelegate>
and have set the delegate to self, in the alertview, like so:
alert_NOT = [[UIAlertView alloc] initWithTitle:#"Not connected!" message:message_to_user delegate:self cancelButtonTitle:#"Try again" otherButtonTitles: nil];
[alert_NOT show];
[alert_NOT release]
and the clickedButtonAtIndex looks like
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"test");
}
So I would love to see the word "test" in the log when a button is pressed in the alertview, but nothing happens.
Update:
Tried implementing it in my "FirstViewController.m" and there it works :S but I would very much like to have it in the appdelegate.m if possible..
I'm currently looking into a similar implementation and would like to share an idea I had with you: perhaps using an NSNotification that fires when your delegate's conditions are met, which can be listened for in your VC(s) and handled appropriately, with an alert view, at the top of the stack.
#interface urAppDelegate : NSObject <UIApplicationDelegate,UIAlertViewDelegate>
If you synthesized the alert_not then use it like this with self:
self.alert_NOT = [[UIAlertView alloc] initWithTitle:#"Not connected!" message:message_to_user delegate:self cancelButtonTitle:#"Try again" otherButtonTitles: nil];
[alert_NOT show];
[alert_NOT release];
You should use the alertViewCancel method for this.
- (void)alertViewCancel:(UIAlertView *)alertView
{
NSLog(#"text");
}
Define as below:
#define appDelegate ((AppDelegate*)[UIApplication sharedApplication].delegate)
and alert as:
UIAlertView *alert_NOT = [[UIAlertView alloc] initWithTitle:#"Not connected!" message:message_to_user delegate:appDelegate cancelButtonTitle:#"Try again" otherButtonTitles: nil];
[alert_NOT show];
Here set delegate as defined keyword, i.e., appDelegate.
Hope this helps.