Сделать блокировку UIAlertView - PullRequest
       2

Сделать блокировку UIAlertView

8 голосов
/ 20 сентября 2010

Мне нужно сделать UIAlertView блокировку. Потому что у меня есть функция, и мне нужно вернуть UIAlertView выбор. Но проблема в том, что после показа UIAlertView мой код функции выполняется дальше, поэтому я не могу поймать UIAlertView выбор (я могу сделать это в методах делегата, но мне нужно вернуть результат функции).

Я пытался сделать UIAlertVIew блокировку с помощью NSCondition. Но код не работает.

condition = [NSCondition new];
result = 0 ; 
[condition lock];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Fingerprint" message:@"test" delegate:window_self cancelButtonTitle:@"No" otherButtonTitles:@"Yes",nil];
[alert setDelegate:self];
[alert show];
while (result == 0) [condition wait];
[condition unlock] ;

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
 [condition lock] ;
 if (buttonIndex == 0)
 {
  result = 2; 
 }
 else if (buttonIndex == 1)
 {
  result = 3 ;
 }
 [condition signal] ;
 [condition unlock] ;
}

Может быть, как исправить этот код или какие-либо другие предложения? Спасибо

Ответы [ 5 ]

8 голосов
/ 21 сентября 2010

Нет способа достичь того, что вы хотите. Только через делегата. Вам следует изменить дизайн своей функции или отказаться от использования UIAlertView

5 голосов
/ 10 мая 2013

Это не делает его блокирующим, но я написал подкласс для добавления синтаксиса стиля блока, который значительно упрощает обработку метода buttonClickedAtIndex без необходимости делать делегат и целый набор операторов if, если у вас есть несколько UIAlertViewв одном классе.

#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

Затем, чтобы вызвать его, приведем пример кода.Другая интересная часть заключается в том, что, поскольку блок закрывается вокруг локальных переменных, я могу получить доступ ко всем состояниям, существовавшим в то время, когда я показываю UIAlertView.Используя традиционный подход делегата, вы должны будете сохранить все это временное состояние в переменные уровня класса, чтобы иметь доступ к нему при вызове buttonClickedAtIndex в делегате.Это намного чище.

{
    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];
}
2 голосов
/ 16 декабря 2011

Я просто столкнулся с той же проблемой. Хотя решения не найдено, есть как минимум 2 обходных пути, о которых я подумал.

Петля "раствор" Сразу после вызова UIAlert вы запускаете цикл, который ищет изменения в переменной, которая является глобальной для вашего объекта (не для всего проекта, обратите внимание), эта переменная - та, которую вы задали в делегате UIAlert, который принимает ответы. Поэтому в основном вы ждете «is A == 1, если не DoEvents» и зацикливаетесь на нем.

Затем на делегате вы делаете A = 1, когда у вас есть ответ

и прежде чем кто-то скажет, что в Какао нет DoEvents:

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];
}

Делегатское решение Вместо того, чтобы иметь код, который имеет дело с Ответом A, B или C, в функции, которая вызывает Alert, используйте код в самом делегате.

Надеюсь, это поможет. Я использовал второй в моем проекте, и он работал.

1 голос
/ 18 сентября 2013

Используйте UIAlertView с блоками от Джозефа и добавьте в него семафор. Объявить глобальный семафор

dispatch_semaphore_t generateNotificationsSemaphore;

И сигнализировать семафор в обработчике блока

[alert showWithHandler:^(UIAlertView *alertView, NSInteger buttonIndex) {
    if (buttonIndex == [alertView cancelButtonIndex]) {

    } else {

    }

    dispatch_semaphore_signal(generateNotificationsSemaphore);
}];

После вызова showWithHandler добавьте цикл ожидания, используя семафор

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

}

Фактическое значение времени ожидания может отличаться в зависимости от ваших потребностей.

1 голос
/ 03 мая 2013

Я только что нашел этот вопрос случайно и даже войдя в адский Apple, разместив его, настоящим провозглашаю это как подтверждение концепции:

@interface EvilShitClass () <UIAlertViewDelegate>
@end

@implementation EvilShitClass {
    BOOL _isCanceled, _wasYes;
}

Вот статический метод для запроса да / нет:

+ (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;
}

и, наконец, метод делегата для обработки нажатия кнопки и остановить runloop-обработку:

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

Это работает, но помните: вы не должны делать это так :-D Довольно, пожалуйста, не спорьте о стиле и прочем, это всего лишь 5 минут быстрого взлома, чтобы доказать, что это может быть сделано! Это должно работать без ARC (new -> autorelease) но если я ошибаюсь, ты знаешь, как с этим справиться;)

Отказ от ответственности: я не несу ответственности за любой возможный ущерб, который использование этого фрагмента может нанести вашему приложению или устройствам. Спасибо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...