DELEGATE NOT TRIGGERING IS A MYSTERY IN IOS

Ankit Singh

Total Post:341

Points:2389
Posted by  Ankit Singh
 1203  View(s)
Ratings:
Rate this:

I have developed this class that is basically a UIAlertView with an input field that runs on blocks. So, whatever the user does on the alertView (click cancel, ok, or fill the text), returns to a block:

HEADER

typedef void (^onClickBlock)(NSInteger buttonIndex, NSString *textoInput);
 
 
@interface AlertViewBlocoComInput : NSObject
 
- (void)mostrarAlertViewBlocoComTitulo:(NSString *)titulo
                              mensagem:(NSString *)mensagem
                     tituloBotaoCancel:(NSString *)tituloCancel
                     outrosBotoesArray:(NSArray *)titulosOutrosBotoes
                   inputComPlaceHolder:(NSString *)textoPlaceholder
                      comBlocoExecucao:(onClickBlock)bloco;
 
@end


IMPLEMENTATION

 

@interface AlertComBloco : UIAlertView
@property (nonatomic, copy) onClickBlock runOnClickBlock;
@end
 
 
@implementation AlertComBloco
@end
 
 
@interface AlertViewBlocoComInput () <UIAlertViewDelegate>
@end
 
 
@implementation AlertViewBlocoComInput
 
 
- (void)mostrarAlertViewBlocoComTitulo:(NSString *)titulo
                              mensagem:(NSString *)mensagem
                     tituloBotaoCancel:(NSString *)tituloCancel
                     outrosBotoesArray:(NSArray *)titulosOutrosBotoes
                   inputComPlaceHolder:(NSString *)textoPlaceholder
                      comBlocoExecucao:(onClickBlock)bloco
 
{
 
  AlertComBloco *alerta = [[AlertComBloco alloc] initWithTitle:titulo
                                                       message:mensagem
                                                      delegate:self
                                             cancelButtonTitle:tituloCancel
                                             otherButtonTitles:nil];
  alerta.runOnClickBlock =bloco;
 
  // adicionar os outros botões
  for (NSString *umOutroBotao in titulosOutrosBotoes) {
    [alerta addButtonWithTitle:umOutroBotao];
  }
 
 
  [alerta setAlertViewStyle:UIAlertViewStylePlainTextInput];
  UITextField *input = [alerta textFieldAtIndex:0];
  input.placeholder = textoPlaceholder;
 
  [alerta show];
 
}
 
 
 
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
 
  AlertComBloco *alerta = (AlertComBloco *)alertView;
  onClickBlock bloco = alerta.runOnClickBlock;
 
  UITextField *input = [alertView textFieldAtIndex:0];
 
  if (bloco) {
    bloco(buttonIndex, input.text);
  }
 
}
 
 
@end




run it, it shows the alertView with the message, placeholder, perfect. I click cancel or fill the text and press ok and the alertview:clickedButtonAtIndex: is never triggered. I am not seeing why.

  1. Kamlakar Singh

    Post:194

    Points:1396
    Re: delegate not triggering is a mystery in ios

    You haven't shown us how you're calling this but most likely your AlertViewBlocoComInput is a local variable that has fallen out of scope and is therefore deallocated before the user completes their input. You can demonstrate this by temporarily adding a dealloc method that will tell you when the object is deallocated.

    - (void)dealloc

                {

                    NSLog(@"%s", __PRETTY_FUNCTION__);

                }

    In terms of fixing this, you could make your AlertViewBlocoComInput instance a class property, so it won't be released until you manually nil it. Or you can make AlertViewBlocoComInput maintain a strong reference to itself until clickedButtonAtIndex is called.

    Or, easiest, leverage the fact that UIAlertView already retains itself, so you can retire AlertComBloco altogether, folding it into AlertViewBlocoComInput:

    typedef void (^onClickBlock)(NSInteger buttonIndex, NSString *textoInput);

     

            @interface AlertViewBlocoComInput : UIAlertView <UIAlertViewDelegate>

     

            - (instancetype)initComTitulo:(NSString *)titulo

                                 mensagem:(NSString *)mensagem

                        tituloBotaoCancel:(NSString *)tituloCancel

                        outrosBotoesArray:(NSArray *)titulosOutrosBotoes

                      inputComPlaceHolder:(NSString *)textoPlaceholder

                         comBlocoExecucao:(onClickBlock)bloco;

     

            @property (nonatomic, copy) onClickBlock runOnClickBlock;

     

            @end

     

            @implementation AlertViewBlocoComInput

     

            - (instancetype)initComTitulo:(NSString *)titulo

                                 mensagem:(NSString *)mensagem

                        tituloBotaoCancel:(NSString *)tituloCancel

                        outrosBotoesArray:(NSArray *)titulosOutrosBotoes

                      inputComPlaceHolder:(NSString *)textoPlaceholder

                         comBlocoExecucao:(onClickBlock)bloco

     

            {

                self = [super initWithTitle:titulo message:mensagem delegate:self cancelButtonTitle:tituloCancel otherButtonTitles:nil];

     

                if (self) {

                    self.runOnClickBlock =bloco;

     

                    // adicionar os outros botões

                    for (NSString *umOutroBotao in titulosOutrosBotoes) {

                        [self addButtonWithTitle:umOutroBotao];

                    }

     

                    [self setAlertViewStyle:UIAlertViewStylePlainTextInput];

                    UITextField *input = [self textFieldAtIndex:0];

                    input.placeholder = textoPlaceholder;

                }

     

                return self;

            }

     

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

            {

                if (self.runOnClickBlock) {

                    UITextField *input = [alertView textFieldAtIndex:0];

                    self.runOnClickBlock(buttonIndex, input.text);

                }

            }

     

            @end

    And you'd call it like so:

    AlertViewBlocoComInput *obj = [[AlertViewBlocoComInput alloc] initComTitulo:@"title" mensagem:@"message" tituloBotaoCancel:@"OK" outrosBotoesArray:nil inputComPlaceHolder:@"Placeholder" comBlocoExecucao:^(NSInteger buttonIndex, NSString *textoInput) {

        // do whatever you want with `buttonIndex` and `textoInput` here

        //

        // NSLog(@"%ld %@", (long)buttonIndex, textoInput);

    }];

    [obj show];

  1. Pawan Shukla

    Post:36

    Points:260
    Re: delegate not triggering is a mystery in ios

    You can directly subclass UIAlertView and add your own initializer. I have abstracted your classes to a single subclass of UIAlertView and it works fine. Please see my posts below,


    typedefvoid
    (^onClickBlock)(NSInteger buttonIndex, NSString *textoInput);
     
            @interface AlertComBloco : UIAlertView<UIAlertViewDelegate>
            @property (nonatomic, copy) onClickBlock runOnClickBlock;
            @end
     
     
            @implementation AlertComBloco 
     
            - (id)initWithTitulo:(NSString *)titulo mensagem:(NSString*)mensagem tituloBotaoCancel:(NSString*)tituloCancel outrosBotoesArray:(NSArray *)titulosOutrosBotoes inputComPlaceHolder:(NSString *)textoPlaceholder
                comBlocoExecucao:(onClickBlock)bloco{
              if(self = [self initWithTitle:titulo message:mensagem delegate:self cancelButtonTitle:tituloCancel otherButtonTitles:nil, nil]){
                _runOnClickBlock = bloco;
                for (NSString *umOutroBotao in titulosOutrosBotoes) {
                  [self addButtonWithTitle:umOutroBotao];
                }
                [self setAlertViewStyle:UIAlertViewStylePlainTextInput];
                UITextField *input = [self textFieldAtIndex:0];
                input.placeholder = textoPlaceholder;
     
              }
              return self;
            } 
     
     
            - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
            {
     
              UITextField *input = [alertView textFieldAtIndex:0];
     
              if (self.runOnClickBlock) {
                self.runOnClickBlock(buttonIndex, input.text);
              }
     
            }
     
            @end

    Then, you could call it with the block in your view controller just like this,

       - (void)showAlert:(id)sender{
      AlertComBloco *alertCombo = [[AlertComBloco alloc] initWithTitulo:@"Hi" mensagem:@"Custom Alert" tituloBotaoCancel:@"Cancel" outrosBotoesArray:@[@"Other"] inputComPlaceHolder:@"Placeholder" comBlocoExecucao:^(NSInteger buttonIndex, NSString *textoInput) {
        NSLog(@"Button at index %ld, with text %@", (long)buttonIndex, textoInput);
      }];
      [alertCombo show];
    }

      Modified On Apr-05-2018 11:56:23 PM

Answer

NEWSLETTER

Enter your email address here always to be updated. We promise not to spam!