cullener
cullener

Reputation: 113

UIAlertView showing twice

I have an application that, in part, loops through the contents of an NSSet and displays a UIAlertView for each item found in the set. When there is only one item in the set, the UIAlertView behaves itself properly. However, if there's more than one, the first view flashes up (normally with the contents of the last item in the set) and then disappears without any user intervention. The first item in the NSSet will then display and wait for a response, before displaying the next item in the NSSet and so on.

It is the same experience as is being described in this unresolved question: IPHONE: UIAlertView called twice in a custom function/IBAction

Here's the code:

#import "CalcViewController.h"

@interface CalcViewController()
@property (nonatomic) int variablesCount;
@property (nonatomic, strong) NSMutableDictionary *variablesSet;
@end

@implementation CalcViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.variablesSet = [[NSMutableDictionary alloc] init];
}


- (IBAction)variablePressed:(UIButton *)sender
{
    [[self calcModel] setVariableAsOperand:sender.titleLabel.text];
    self.expressionDisplay.text = [[self calcModel] descriptionOfExpression:self.calcModel.expression];
}

- (IBAction)solveExpressionPressed:(UIButton *)sender {
    self.variablesCount = 0;
    [self.variablesSet removeAllObjects];

    NSSet *variablesCurrentlyInExpression = [[NSSet alloc] initWithSet:[CalcModel variablesInExpression:self.calcModel.expression]];
    self.variablesCount = [variablesCurrentlyInExpression count];

    if (variablesCurrentlyInExpression){
        for (NSString *item in variablesCurrentlyInExpression) {
            UIAlertView *alertDialog;
            alertDialog = [[UIAlertView alloc] initWithTitle:@"Enter value for variable"
                                                message:item
                                                delegate:self
                                                cancelButtonTitle:@"OK"
                                                otherButtonTitles:nil];

            alertDialog.alertViewStyle=UIAlertViewStylePlainTextInput;
            UITextField * alertTextField = [alertDialog textFieldAtIndex:0];
            alertTextField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
            [alertDialog show];
        }

    }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
      if (buttonIndex == 0){
        if ([[alertView textFieldAtIndex:0] text]){         
            self.variablesSet[alertView.message] = [[alertView textFieldAtIndex:0] text];
        }
    }

    if ([self.variablesSet count] == self.variablesCount){
        NSLog(@"time to solve");
        [[self calcDisplay] setText:[NSString stringWithFormat:@"%g", [CalcModel evaluateExpression:self.calcModel.expression usingVariableValues:self.variablesSet]]];
    }
}

I've checked the IBActions behind the button that triggers the solveExpressionPressed method and that is the only one that exists. I've also placed some logging before the [alertDialog show]; line and it is only called twice when the variablesCurrentlyInExpression NSSet contains two values, yet the UIAlertView appears three times (flashing up once).

Finally, i've tried it without the following code:

            UITextField * alertTextField = [alertDialog textFieldAtIndex:0];
            alertTextField.keyboardType = UIKeyboardTypeNumbersAndPunctuation;

and the problem still occurs, so I don't think it's that.

I've been stuck on this a while and haven't figured it out (hence the post!!), so any help would be greatly appreciated.

Thanks

Upvotes: 0

Views: 1123

Answers (3)

danh
danh

Reputation: 62686

To get this done, you'll need to keep some extra state in your class, like this...

@property (strong, nonatomic) NSMutableSet *promptVariables;
@property (strong, nonatomic) NSString *promptVariable;
@property (strong, nonatomic) NSMutableDictionary *promptResults;

You can probably get away with less by keeping some in your model as it is (or hiding a little in the alert view message as you cleverly do currently), but I'll use all new variables for clarity.

When you want to make several prompts, set up your state like this...

self.promptVariables = [[NSSet alloc] initWithSet:[CalcModel variablesInExpression:self.calcModel.expression]];
[self promptForVariables];

Define promptForVariables to bail if it has no work to do (promptVariables is empty) or remove one and do the alert for it.

- (void)promptForVariables {

    if (![self.promptVariables count]) return;
    self.promptResults = [NSMutableDictionary dictionary];

    self.promptVariable = [self.promptVariables anyObject];
    [self.promptVariables removeObject:self.promptVariable];

    // do your alert here, I won't repeat your code
}

Then when the alert is done, process the result as you have and call promptForVariables again. The next time, since you've changed state, it has less work to do.

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
      if (buttonIndex == 0){
        if ([[alertView textFieldAtIndex:0] text]){         
            [self.promptResults setValue:[[alertView textFieldAtIndex:0] text] forKey:self.promptVariable];
        }
        [self performSelector:@selector(promptForVariables) withObject:nil afterDelay:0.0];
    }
}

When this is done, promptResults will contain variable names as keys and user inputs as values.

Upvotes: 0

Totumus Maximus
Totumus Maximus

Reputation: 7573

Easily fixed with a boolean flag that you set to YES when the first alert is shown. Then when the second match is found and the boolean is already YES because the alert is visible you won't show it. Then again you might want to know the exact amount of matches in the NSSet. In that case you keep track with a counter and show the alert after the match function has been done and the counter is not 0.

Avoid showing the alert inside the method of the button trigger. Instead split every function into different sets of methods. Not just for making your function work but maintainability of the code later.

Upvotes: 1

W Dyson
W Dyson

Reputation: 4634

Try showing the first UIAlertView and then showing the second after the first is dismissed.

What's happens is if an app or the OS calls [alert show] and a UIAlertView is already being displayed, the original alertView gets placed in a queue and the new one is presented. When the new one is dismissed, the original UIAlertView is re-shown.

Hope this helps

Upvotes: 1

Related Questions