2009-09-03 82 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的辯論,我同意蘋果的實施 - 以免違規。然而,在這種情況下,我沒有看到通過將代碼放在-alertView:clickedButtonAtIndex中解決問題的任何方式,因爲我不需要基於UIAlertView運行代碼,我只需要閱讀響應。關於我如何能夠進入監獄的任何建議?我已經在[alert show]之後嘗試了一段時間循環,但之後警報甚至沒有顯示,出於多種原因,我無法使用-viewWillDisapear。

編輯

對於那些觀看在現代的ios時代這個問題,這個問題涉及到iOS 2

回答

10

不僅UIAlertView的顯示不等待用戶觸摸按鈕,它甚至不等待警報視圖淡入視圖。它立即返回

爲班級添加一個標誌。如果不是,則從readyToNavigateAwayFromTab返回NO並顯示您的警報。在clickedButtonAtIndex中,設置標誌以便readyToNavigateAwayFromTab知道返回YES。仍然在clickedButtonAtIndex中,重試代碼中的標籤導航。

+0

輝煌的解決方案。華麗的旗幟!謝謝。 – JoBu1324 2010-04-20 18:20:26

+0

哇。我從未想過你會回來,我的回答太晚了。很高興幫助。 :) – 2010-04-20 18:50:03

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沒有任何效果。你確定[alert show]應該等到警報消失嗎?到處都是我看過的人都試圖以模態的方式讓UIAlertView行爲。 – JoBu1324 2009-09-03 20:01:50

+0

如果[alert show]工作不正常,我會建議在XCode中創建一個新項目並在此測試項目中對UIAlertView進行測試。這是更多的工作,授予,但它會幫助你解決你現有的問題。 – 2009-09-03 20:51:25

+0

好的,我從UICatalog中取出蘋果代碼,並在[alert show]之後插入NSLog()消息,但NSLog消息在解除警報之前觸發。在調用警報方法後,我甚至放了另一個NSLog調用,但是這個消息在解除警報之前也運行,所以仍然沒有快樂。我討厭聽起來令人懷疑,但我現在2分0。你確定[alert show]應該暫停執行它所調用的方法中的代碼嗎?你是否有一些你可以發佈的代碼是按照你聲稱的方式工作的?謝謝,jb – JoBu1324 2009-09-03 21:08:21

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 2012-02-10 21:40:53

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.hBSAlertViewDelegateBlock.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