2009-09-03 2 views
4

Один из моих методов отправляет сообщение объекту (что вам известно об этом), и ожидает ответа BOOL. Однако ответ BOOL, который он ожидает, основан на ответе на UIAlertView, созданный в методе принимающего объекта. Тем не менее, код не останавливается, ожидая ответа пользователя на UIAlertView. Моя проблема: как использовать -alertView: clickedButtonAtIndex в возвращаемом значении метода?Приостановить выполнение кода до нажатия кнопки UIAlertView?

Вот код работает сообщение (в этой конструкции, я ожидал navigateAwayFromTab меняться в зависимости от пользовательского ввода в UIAlertView, но он никогда не получает шанс):

- (BOOL)readyToNavigateAwayFromTab { 
    NSLog(@"message received by Medical View"); 
    navigateAwayFromTab = NO; 
    UIAlertView *alert = [[UIAlertView alloc] 
      initWithTitle:@"Navigate Away From Tab?" 
       message:@"Navigating away from this tab will save your work." 
       delegate:self 
     cancelButtonTitle:@"Cancel" 
     otherButtonTitles:@"OK", nil 
    ]; 
    [alert show]; 
    [alert release]; 
    return navigateAwayFromTab; 
} 
#define CANCEL 0 
#define OK 1 
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { 
    if(buttonIndex == OK) navigateAwayFromTab = YES; 
} 

Я читала по модальным дискуссиям UIAlertView, и я согласен с реализацией Apple - так как это правило. Однако в этом случае я не вижу способа решить проблему, поставив код в -alertView: clickedButtonAtIndex, потому что мне не нужно запускать код на основе UIAlertView, мне просто нужно прочитать ответ. Любые предложения о том, как я могу добраться до моего тюрьмы? Я уже пробовал цикл while после [alert show], но тогда предупреждение даже не отображается, и по ряду причин я не могу использовать -viewWillDisapear.

Редактировать

Для тех просмотра этого вопроса в современных эпохах Ios, этот вопрос относился к Ios 2

ответ

10

Не только шоу UIAlertView не дожидается, пока пользователь коснется кнопки, он даже не дожидается, когда вид предупреждения будет исчезать. Он возвращает сразу.

Добавить флаг в ваш класс. Если это НЕТ, верните NO из readyToNavigateAwayFromTab и покажите свое предупреждение. В clickedButtonAtIndex установите флаг, чтобы команда readyToNavigateAwayFromTab знала, что возвращает YES. Все еще в clickedButtonAtIndex, повторите навигацию на вкладке из кода.

+0

Блестящее решение. Ура для флагов! Благодарю. – JoBu1324

+0

Ничего себе. Я никогда не думал, что ты вернешься, мой ответ был настолько запоздалым. Рад, что это помогло. :) –

0

[alert show] заявления должны задержать приложения пока ответ не предоставляются.

Подключен ли ваш контроллер к протоколу UIAlertViewDelegate? Проверьте, если вам нужно добавить <UIAlertViewDelegate> в файл заголовок контроллера, например:

@interface RootViewController : UIViewController <UIAlertViewDelegate, UITabBarDelegate, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate> 

Вы можете также сделать свой navigateAwayFromTab переменным свойства, например:

@interface RootViewController : UIViewController <UIAlertViewDelegate> { 
    BOOL navigateAwayFromTab; 
} 

@property BOOL navigateAwayFromTab; 

... 

@end 

В реализации:

@implementation RootViewController 

@synthesize navigateAwayFromTab; 

... 

- (void) readyToNavigateAwayFromTab { 
    UIAlertView *alert = [[UIAlertView alloc] 
     initWithTitle:@"Navigate Away From Tab?" 
      message:@"Navigating away from this tab will save your work." 
      delegate:self 
    cancelButtonTitle:@"Cancel" 
    otherButtonTitles:@"OK", nil 
    ]; 
    [alert show]; 
    [alert release]; 
} 

#define CANCEL 0 
#define OK 1  
- (void) alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { 
    if ([actionSheet.title compare:@"Navigate Away From Tab?"] == NSOrderedSame) { 
    if (buttonIndex == OK) 
     self.navigateAwayFromTab = YES; 
    else 
     self.navigateAwayFromTab = NO; 
    } 
} 
+0

Спасибо за ответ! Я подписал контракт с UIAlertViewDelegate. Вы уверены, что [предупреждение] должно ждать, пока предупреждение не исчезнет? Везде, где я смотрел, я видел, как люди пытаются заставить UIAlertView действовать модально. – JoBu1324

+0

Если [alert show] работает неправильно, я бы предложил создать новый проект в XCode и выполнить тестовый UIAlertView в этом тестовом проекте. Это больше работы, предоставляется, но это поможет вам устранить ваши существующие проблемы. –

+0

Хорошо, я взял код Apple из UICatalog и вставил сообщение NSLog() после их [предупреждения], но сообщение NSLog уволилось, прежде чем отклонить предупреждение. Я даже разместил еще один вызов NSLog после вызова метода оповещения, но это сообщение также запускалось до отклонения предупреждения, так что до сих пор нет радости. Я ненавижу звучать сомнительно, но сейчас я за 2 сейчас. Вы уверены, что [предупреждение] должно приостановить выполнение кода в методе, в котором он вызван? У вас есть код, который вы можете опубликовать, который работает так, как вы утверждаете? Спасибо, jb – JoBu1324

4

Решение с использованием NSCondition, когда запуск от фонового потока:

// on background thread 
condition = [NSCondition new]; 
questionAnswered = NO; 
// display UIAlertView in a method on main thread with -performSelectorOnMainThread:withObject:waitUntilDone: 
[condition lock]; 
while (! questionAnswered) [condition wait]; 
questionAnswered = NO; 
[condition unlock]; 
[condition release]; 

// on main thread in delegate method -alertView:clickedButtonAtIndex: 
// (do something with choosen buttonIndex) 
questionAnswered = YES; 
[condition signal]; 
[condition unlock] 

Рафаэль

+0

+1 потому что это фактически ведет вас по правильной дороге, которая для меня должна была создать собственный класс AlertView, который вызывает UIAlertView + MKBlockAdditions.h, но в моем классе AlertView я завернул свой вызов MKUIAlertView в приведенном выше коде потока. , единственное, что я заметил, это то, что вы разблокируете поток пару раз, что вызывает некоторые ошибки. – J3RM

1

Лучшее решение Я находку от Sasmito Adibowo в своем блоге Painless UIAlertView. Он создал BSAlertViewDelegateBlock, общую реализацию UIAlertViewDelegate, которая позволяет использовать закрытие в качестве обработчика делегата.

Вот как вы его используете:

UIAlertView* alert = ...; 
BSAlertViewDelegateBlock* alertDelegate = [[BSAlertViewDelegateBlock alloc] initWithAlertView:alert]; 
alertDelegate.didDismissWithButtonIndexBlock = ^(UIAlertView* alertView, NSInteger buttonIndex) 
{ 
    switch (buttonIndex) 
    { 
     ... 
    } 
}; 
[alert show]; 

Реализация BSAlertViewDelegateBlock.h и BSAlertViewDelegateBlock.m доступны на GitHub, но я повторно отправил код здесь:

BSAlertViewDelegateBlock.h

#import <UIKit/UIKit.h> 

/** 
Adapts UIAlertViewDelegate protocol into a block-friendly interface. 
*/ 
@interface BSAlertViewDelegateBlock : NSObject<UIAlertViewDelegate> 

/** 
Designated initializer. 
*/ 
-(id) initWithAlertView:(UIAlertView*) alertView; 

@property (nonatomic,strong) void(^clickedButtonAtIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex); 
@property (nonatomic,strong) void(^alertViewCancelBlock)(UIAlertView* alertView); 
@property (nonatomic,strong) void(^willPresentAlertViewBlock)(UIAlertView* alertView); 
@property (nonatomic,strong) void(^didPresentAlertViewBlock)(UIAlertView* alertView); 

@property (nonatomic,strong) void(^willDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex); 
@property (nonatomic,strong) void(^didDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex); 

@property (nonatomic,strong) BOOL(^alertViewShouldEnableFirstOtherButtonBlock)(UIAlertView* alertView); 

@end 

BSAlertViewDelegateBlock.m

#import <objc/runtime.h> 

#import "BSAlertViewDelegateBlock.h" 

const char* const BSAlertViewDelegateBlockKey = "BSAlertViewDelegateBlockKey"; 

@interface BSAlertViewDelegateBlock() 

@property (nonatomic,weak) UIAlertView* alertView; 

@end 

@implementation BSAlertViewDelegateBlock 

-(void) alertViewDelegateBlock_cleanup 
{ 
UIAlertView* alertView = self.alertView; 
// remove all handler blocks in case one of these blocks inadvertendly caused a circular reference to this delegate object. 
self.clickedButtonAtIndexBlock = nil; 
self.alertViewCancelBlock = nil; 
self.willPresentAlertViewBlock = nil; 
self.didPresentAlertViewBlock = nil; 
self.willDismissWithButtonIndexBlock = nil; 
self.didDismissWithButtonIndexBlock = nil; 
self.alertViewShouldEnableFirstOtherButtonBlock = nil; 
// finally remove this delegate from the alert view 
if (alertView.delegate == self) { 
alertView.delegate = nil; 
objc_setAssociatedObject(alertView, BSAlertViewDelegateBlockKey, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
} 
} 

-(id) initWithAlertView:(UIAlertView *)alertView 
{ 
if (self = [super init]) { 
self.alertView = alertView; 
objc_setAssociatedObject(alertView, BSAlertViewDelegateBlockKey, self, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 
alertView.delegate = self; 
} 
return self; 
} 


#pragma mark UIAlertViewDelegate 

// Called when a button is clicked. The view will be automatically dismissed after this call returns 
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 
{ 
void(^clickedButtonAtIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex) = self.clickedButtonAtIndexBlock; 
if (clickedButtonAtIndexBlock) { 
clickedButtonAtIndexBlock(alertView,buttonIndex); 
} 
} 

// Called when we cancel a view (eg. the user clicks the Home button). This is not called when the user clicks the cancel button. 
// If not defined in the delegate, we simulate a click in the cancel button 
- (void)alertViewCancel:(UIAlertView *)alertView 
{ 
void(^alertViewCancelBlock)(UIAlertView* alertView) = self.alertViewCancelBlock; 
if (alertViewCancelBlock) { 
alertViewCancelBlock(alertView); 
} 
[self alertViewDelegateBlock_cleanup]; 
} 

- (void)willPresentAlertView:(UIAlertView *)alertView 
{ 
void(^willPresentAlertViewBlock)(UIAlertView* alertView) = self.willPresentAlertViewBlock; 
if (willPresentAlertViewBlock) { 
willPresentAlertViewBlock(alertView); 
} 
} 


- (void)didPresentAlertView:(UIAlertView *)alertView 
{ 
void(^didPresentAlertViewBlock)(UIAlertView* alertView) = self.didPresentAlertViewBlock; 
if (didPresentAlertViewBlock) { 
didPresentAlertViewBlock(alertView); 
} 
} 


- (void)alertView:(UIAlertView *)alertView willDismissWithButtonIndex:(NSInteger)buttonIndex 
{ 
void(^willDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex) = self.willDismissWithButtonIndexBlock; 
if (willDismissWithButtonIndexBlock) { 
willDismissWithButtonIndexBlock(alertView,buttonIndex); 
} 
} 


- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex 
{ 
void(^didDismissWithButtonIndexBlock)(UIAlertView* alertView,NSInteger buttonIndex) = self.didDismissWithButtonIndexBlock; 
if (didDismissWithButtonIndexBlock) { 
didDismissWithButtonIndexBlock(alertView,buttonIndex); 
} 
[self alertViewDelegateBlock_cleanup]; 
} 


- (BOOL)alertViewShouldEnableFirstOtherButton:(UIAlertView *)alertView 
{ 
BOOL(^alertViewShouldEnableFirstOtherButtonBlock)(UIAlertView* alertView) = self.alertViewShouldEnableFirstOtherButtonBlock; 
if (alertViewShouldEnableFirstOtherButtonBlock) { 
return alertViewShouldEnableFirstOtherButtonBlock(alertView); 
} 

// default to enable. 
return YES; 
} 


@end 
Смежные вопросы