Search code examples
ios6uitextfielduialertviewobjective-c-blocksobjective-c-category

How to access user input from UIAlertView completion block without delegation?


Using iOS6:

I would like to retrieve the text entered by a user into a UITextField associated with the UIAlertView. I am aware that I could achieve the desired result with a delegate however I am curious about solving this issue with a callback function as I believe this may be an interesting pattern. I began by examining a common pattern for category extension of the UIAlertView class. Code below. Thanks in advance for any suggestions.

import <UIKit/UIKit.h>

@interface UIAlertView (Block)
- (id)initWithTitle:(NSString *)title message:(NSString *)message completion:(void (^)(BOOL cancelled,     NSInteger buttonIndex, UITextField *textField))completion cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;

@end

The .m for the category follows:

#import "UIAlertView+Block.h"
#import <objc/runtime.h>


static char const * const alertCompletionBlockTag = "alertCompletionBlock";

@implementation UIAlertView (Block)

- (id)initWithTitle:(NSString *)title
            message:(NSString *)message
         completion:(void (^)(BOOL cancelled, NSInteger buttonIndex))completion
  cancelButtonTitle:(NSString *)cancelButtonTitle
  otherButtonTitles:(NSString *)otherButtonTitles, ... {

self = [self initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil ];


if (self) {
    objc_setAssociatedObject(self, alertCompletionBlockTag, completion, OBJC_ASSOCIATION_COPY);

    va_list _arguments;
    va_start(_arguments, otherButtonTitles);

    for (NSString *key = otherButtonTitles; key != nil; key = (__bridge NSString *)va_arg(_arguments, void *)) {
        [self addButtonWithTitle:key];
    }
    va_end(_arguments);
}

[self setAlertViewStyle:UIAlertViewStylePlainTextInput];
return self;
}


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

id completion = objc_getAssociatedObject(self, alertCompletionBlockTag);

[self complete:completion index:buttonIndex];
}

- (void) complete:(void (^)(BOOL cancelled, NSInteger buttonIndex))block index:(NSInteger)buttonIndex {
BOOL _cancelled = (buttonIndex == self.cancelButtonIndex);
block(_cancelled, buttonIndex );

objc_setAssociatedObject(self, alertCompletionBlockTag, nil, OBJC_ASSOCIATION_COPY);
//objc_removeAssociatedObjects(block);
}


@end

Usage for the category is set below. The main problem is my inability to reference the UIAlertView textField at Index 0 from within the completion block.

[[[UIAlertView alloc] initWithTitle:@"Add"
                           message:@"Add New Asset Type"
                        completion:^(BOOL cancelled, NSInteger buttonIndex){
                            if (!cancelled) {

                                //call on completion of UISheetAction ???
                                NSLog(@"%@",needToAccessUIAlertView._textFields[0]);



                            }
                        }
                 cancelButtonTitle:@"Cancel"
                 otherButtonTitles:@"OK", nil] show];

Solution

  • So basically you want to access the alert view from the block. You can do something like this:

    __block __weak UIAlertView *alertViewWeak;
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Add"
                               message:@"Add New Asset Type"
                            completion:^(BOOL cancelled, NSInteger buttonIndex){
                                if (!cancelled) {
    
                                    //call on completion of UISheetAction ???
                                    NSLog(@"%@",[alertViewWeak textFieldAtIndex:0]);
    
    
    
                                }
                            }
                     cancelButtonTitle:@"Cancel"
                     otherButtonTitles:@"OK", nil];
    alertViewWeak = alertView;
    [alertView show];