24

How do you pass a variable to the UIAlertView delegate?

I have a variable that I want to use in the alert view delegate. It is only used in the function that shows the UIAlertView and the UIAlertView delegate, so i don't think it should be a property on the controller. Is there a way to attach the variable to UIAlertView and retrieve it in the delegate?

- (void) someUserCondition:(SOCode *)userCode {
    if ([userCode warrentsConfirmation] > 0) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"Are you sure?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK",nil];        
        [alert setAlertViewStyle:UIAlertViewStyleDefault];  
        //TODO somehow store the code variable on the alert view
        [alert show];
    }
}

- (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex   {
    NSString *title = [alertView buttonTitleAtIndex:buttonIndex];
    if ([title isEqualToString:@"OK"]){
       SOCode *userCode = //TODO somehow get the code from the alert view
       [self continueWithCode:code];
    }                                 
}
etolstoy
  • 1,798
  • 21
  • 33
wusher
  • 12,291
  • 22
  • 72
  • 95
  • 2
    possible duplicate of [How to add userInfo to a UIAlertView?](http://stackoverflow.com/questions/5279884/how-to-add-userinfo-to-a-uialertview) – jscs Apr 30 '12 at 17:56

6 Answers6

26

in .h before interface:

extern const char MyConstantKey;
@interface ViewController...

in .m import:

import <objc/runtime.h>

in .m before implementation

const char MyConstantKey;

in .m implementation

-(void)viewDidAppear:(BOOL)animated{ //or wherever

    NSString *aString = @"This is a string";

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Testing" message:@"test is test" delegate:self cancelButtonTitle:@"Okay" otherButtonTitles:nil];

    [alert show];

    [alert release];

    objc_setAssociatedObject(alert, &MyConstantKey, aString, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

 }

in .m alertview callback

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{

     NSString *associatedString = objc_getAssociatedObject(alertView, &MyConstantKey);

     NSLog(@"associated string: %@", associatedString);

}
Priebe
  • 4,942
  • 3
  • 24
  • 38
warpedspeed
  • 1,098
  • 11
  • 28
  • Why do we need 'extern const char MyConstantKey' in .h as well as const char MyConstantKey in .m? – Srinivasan N Sep 12 '14 at 06:02
  • When using this solution just be aware of the following issue http://stackoverflow.com/questions/21561211/objc-setassociatedobject-function-error-in-64bit-mode-not-in-32bit – ben-efiz Sep 24 '15 at 18:32
  • i got Undefined symbols for architecture x86_64: "_MyConstantKey", referenced from: – kemdo Mar 27 '18 at 08:02
14

Use Associated Objects. It is described in more detail here: Your New Friends: Obj-C Associated Objects

To set the object you use use:

objc_setAssociatedObject(alert, &key, userCode, OBJC_ASSOCIATION_RETAIN);

And then to get it back:

SOCode *userCode = objc_getAssociatedObject(alertView, &key);

You also need to add static char key; so that it is in the scope of moth methods.

Update

I have wrapped this into a category on UIAlertView. You can use Cocoapods to bring it in:

pod 'HCViews/UIAlertViewHCContext', '~> 1.2'

The source is available here: https://github.com/hypercrypt/HCViews/blob/master/Categories/UIAlertView%2BHCContext.h

Community
  • 1
  • 1
hypercrypt
  • 15,389
  • 6
  • 48
  • 59
7

A lot of posts talk about the concepts behind associated objects (which is good!) but sometimes you just want to see the code. Here's a clean and quick category that you can either put in a separate file or above the interface of one of your existing .m files (you could even replace UIAlertView with NSObject and effectively add a context property to any object):

#import <objc/runtime.h>

@interface UIAlertView (Private)
@property (nonatomic, strong) id context;
@end

@implementation UIAlertView (Private)
@dynamic context;
-(void)setContext:(id)context {
    objc_setAssociatedObject(self, @selector(context), context, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(id)context {
    return objc_getAssociatedObject(self, @selector(context));
}
@end

And then you'll be able to do something like:

NSObject *myObject = [NSObject new];

UIAlertView *alertView = ...
alertView.context = myObject;

IMPORTANT: And don't forget to nil the context in dealloc!!

lobianco
  • 6,226
  • 2
  • 33
  • 48
3

UIAlertView is a subclass of UIView which has a tag property you can set to an integer. Unfortunately if you need something other than an integer to identify/pass info to the delegate than you will need to set some properties (or set up an array with the tag indexing into it) on the delegate itself. Advaith's way will probably work but is technically not supported by Apple.

    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"Are you sure?" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK",nil];        
    [alert setAlertViewStyle:UIAlertViewStyleDefault];  
    alert.tag = SOMEINTEGER;
    [alert show];
Russ
  • 1,786
  • 13
  • 17
  • 1
    This is quite good for a fast solution to the problem BUT what if: 1.you do not have to pass an integer but a string and 2.what if you have another alert view with different actions and you would like to write its actions based on its tags? – arniotaki Nov 04 '14 at 07:59
1

I suspect the most straight-forward way is a property in the alert view's delegate class. An alert view doesn't have any provision for "user info" and doesn't support sub-classing, which removes the only shortcuts that come to mind.

Phillip Mills
  • 30,888
  • 4
  • 42
  • 57
  • 1
    It certainly does support subclassing. – Jonathan Grynspan Apr 30 '12 at 20:46
  • 8
    [UIAlertView Doc](http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIAlertView_Class/UIAlertView/UIAlertView.html) -- _The UIAlertView class is intended to be used as-is and does not support subclassing_ – Jeffery Thomas Apr 30 '12 at 20:53
  • @commentators: He didn't suggest subclassing UIAlertView, but adding a property to the delegate class (for example, the ViewController that is using the AlertView). It's a valid option in my opinion. – avf Jun 28 '13 at 09:32
0

Subclass UIAlertView, add a property called userInfo with type of your choice. Set the user info value at the time you create an instance of Subclassed UIAlertView, and retrieve it from within the delegate method. (There you will get the subclassed instance which holds the userInfo)

Advaith
  • 1,087
  • 3
  • 12
  • 31
  • 5
    You should not subclass `UIAlertView`. – hypercrypt Apr 30 '12 at 20:56
  • @hypercrypt: I think I know why you say that UIAlertView should not be subclassed but could you share your reasons for saying that? (NVM a comment below spells it out) – Mattia Apr 30 '12 at 21:05
  • @hypercrypt Why do you say that we should not subclass UIAlertView. I tried an eg and it worked cool! – Advaith May 01 '12 at 04:26
  • 1
    As Jeffery Thomas also posted below, [UIAlertView Class Reference](http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIAlertView_Class/UIAlertView/UIAlertView.html): The UIAlertView class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified. – hypercrypt May 01 '12 at 08:12
  • In addition to my comment above, while it may work on iOS 5 it may not on iOS 6 or 7 or 8. As Apple has explicitly told you not to subclass `UIAlertView` they reserve the right to change it in the future, for example by turning it into a class-cluster. If they did then your code will definitely break. – hypercrypt May 01 '12 at 08:17