Make UIAlertView blocking

后端 未结 5 2044
刺人心
刺人心 2020-12-03 12:49

I need make UIAlertView blocking. Because i have function and i need to return UIAlertView choice. But problem is that after UIAlertView

相关标签:
5条回答
  • 2020-12-03 12:56

    I was just facing the same problem. Although no solution, there are at least 2 workarounds that I thought of.

    Loop "solution" Right after you call the UIAlert you start a loop that looks for the change in a variable that is global to your object (not to the whole project, mind you) that variable is the one you set in the UIAlert delegate that takes the answers. So basically you wait for "is A == 1, if not DoEvents" and loop on it.

    Then on the delegate you make A=1 when you have the answer

    and before someone says that there is no DoEvents in Cocoa:

    void MyTestClass::DoEvents()
    {
    
      NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
      NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
        untilDate:[NSDate distantPast]
        inMode:NSDefaultRunLoopMode
        dequeue:YES];
      if (event) {
        [NSApp sendEvent:event];
        [NSApp updateWindows];
      }
      [pool release];
    }
    

    Delegate Solution Instead of having the code that deals with Answer A, B or C in the function that calls the Alert, have the code in the delegate itself.

    Hope it helps. I used the second one in my project and it worked.

    0 讨论(0)
  • 2020-12-03 12:59

    I just found this question by accident and even by entering Apple hell by posting this, I hereby proclaim this as a proof of concept:

    @interface EvilShitClass () <UIAlertViewDelegate>
    @end
    
    @implementation EvilShitClass {
        BOOL _isCanceled, _wasYes;
    }
    

    Here is the static method for a yes/no query:

    + (BOOL)yesNoQueryWithTitle:(NSString*)title text:(NSString*)text {
    
        EvilShitClass *shit = [EvilShitClass new];
        UIAlertView *alertView = [UIAlertView new];
        alertView.delegate = shit;
        alertView.title = title;
        alertView.message = text;
        [alertView addButtonWithTitle:@"Yes"];
        [alertView addButtonWithTitle:@"No"];
    
        NSRunLoop *run_loop = [NSRunLoop currentRunLoop];
    
        [alertView show];
    
        while( !shit->_isCanceled ) {
            BOOL tmp = [run_loop runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
        }
    
        return shit->_wasYes;
    }
    

    and finally the delegate method for handling the button click and stop the runloop-processing:

    - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
        _wasYes = (buttonIndex == 0);
        _isCanceled = YES;
    }
    

    This works but remember: you shouldn't do it this way :-D Pretty please don't argue about style and stuff, it's just a 5 minutes quick hack to proof it can be done! This should work without ARC (new -> autorelease) but if I'm wrong you know how to handle it ;)

    Disclaimer: I'm not responsible for any possible damage the use of this snippet could do to your application or devices. Thank you.

    0 讨论(0)
  • 2020-12-03 13:03

    Use the UIAlertView with blocks from Joseph and add a semaphore to it. Declare a global semaphore

    dispatch_semaphore_t generateNotificationsSemaphore;
    

    And signal the semaphore in the block handler

    [alert showWithHandler:^(UIAlertView *alertView, NSInteger buttonIndex) {
        if (buttonIndex == [alertView cancelButtonIndex]) {
    
        } else {
    
        }
    
        dispatch_semaphore_signal(generateNotificationsSemaphore);
    }];
    

    After calling the showWithHandler add a waiting loop using the semaphore

    while (dispatch_semaphore_wait(generateNotificationsSemaphore, DISPATCH_TIME_NOW )) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:20]];
    
    }
    

    Your actual timeout value may be different depending on your needs.

    0 讨论(0)
  • 2020-12-03 13:06

    There's no way to achieve what you want. Only through the delegate. You should redesign your function or refuse using UIAlertView

    0 讨论(0)
  • 2020-12-03 13:12

    This doesn't make it blocking, but I have written a subclass to add block style syntax which makes it much easier to handle the buttonClickedAtIndex method without having to do a delegate and a whole bunch of if statements if you have multiple UIAlertViews in one class.

    #import <UIKit/UIKit.h>
    
    @interface UIAlertViewBlock : UIAlertView<UIAlertViewDelegate>
    - (id) initWithTitle:(NSString *)title message:(NSString *)message block: (void (^)(NSInteger buttonIndex))block
       cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_AVAILABLE(10_6, 4_0);
    @end
    
    
    #import "UIAlertViewBlock.h"
    
    @interface UIAlertViewBlock()
    {
        void (^_block)(NSInteger);
    }
    @end
    
    @implementation UIAlertViewBlock
    
    - (id) initWithTitle:(NSString *)title message:(NSString *)message block: (void (^)(NSInteger buttonIndex))block
    cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_AVAILABLE(10_6, 4_0)
    {
        if (self = [super initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:otherButtonTitles, nil])
        {
            _block = block;
        }
        return self;
    }
    
    - (void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
    {
        _block(buttonIndex);
    }
    
    @end
    

    Then to call it here is some example code. The other cool part is that because a block closes around the local variables, I can have access to all the state that existed at the time I show the UIAlertView. Using the traditional delegate approach, you would have to store all that temporary state into class level variables to have access to it in the call to buttonClickedAtIndex in the delegate. This is so much cleaner.

    {
        NSString *value = @"some random value";
        UIAlertViewBlock *b = [[UIAlertViewBlock alloc] initWithTitle:@"Title" message:@"Message" block:^(NSInteger buttonIndex)
            {
                if (buttonIndex == 0)
                    NSLog(@"%@", [value stringByAppendingString: @" Cancel pressed"]);
                else if (buttonIndex == 1)
                    NSLog(@"Other pressed");
                else
                    NSLog(@"Something else pressed");
            }
            cancelButtonTitle:@"Cancel" otherButtonTitles:@"Other", nil];
    
        [b show];
    }
    
    0 讨论(0)
提交回复
热议问题