我的一个方法向对象发送一条消息(你对此有何了解),并希望得到一个BOOL答案。但是,BOOL回答它的期望是基于在接收对象的方法中创建的UIAlertView的答案。但是,代码在等待用户回答UIAlertView时不会暂停。我的问题是:如何在方法的返回值中使用-alertView:clickedButtonAtIndex?
这是消息运行的代码(在这种结构中,我期望navigationAwayFromTab根据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有关。
答案 0 :(得分:10)
UIAlertView的节目不仅没有等待用户触摸按钮,它甚至不等待警报视图淡入视图。它立即返回 。
为您的班级添加一个标志。如果为NO,则从readyToNavigateAwayFromTab返回NO并显示您的警报。在clickedButtonAtIndex中,设置标志,以便readyToNavigateAwayFromTab知道返回YES。仍然在clickedButtonAtIndex中,从代码重试标签导航。
答案 1 :(得分: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]
圣拉斐尔
答案 2 :(得分: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];
GitHub上提供了BSAlertViewDelegateBlock.h和BSAlertViewDelegateBlock.m的实现,但我已在此处重新发布了代码:
#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
#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
答案 3 :(得分: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;
}
}