按顺序显示 UIAlertViews

Posted

技术标签:

【中文标题】按顺序显示 UIAlertViews【英文标题】:Displaying UIAlertViews Sequentially 【发布时间】:2011-05-18 13:43:28 【问题描述】:

我有几个 UIAlertViews 我想按顺序显示,并且只有在前一个 UIAlertView 被关闭后才继续显示下一个 UIAlertView(通过用户单击确定)。

我知道 didDismissWithButtonIndex 委托和添加标签,但这并没有太大帮助,因为可能会调用多达 3 个 UIAlertViews,并且不一定每次都以相同的顺序。见代码:

if(condition 1)
    alert1 = // UIAlertView[[.....
    [alert1 show]


if(condition 2)
    alert2 = // UIAlertView[[.....
    [alert2 show]


if(condition 3)
    alert3 = // UIAlertView[[.....
    [alert3 show]

上面只会添加 3 个警报(取决于满足多少条件),这不是我想要的。我希望一次只显示一个,然后在用户点击确定按钮后显示下一个(如果有的话)。

我的想法可能是将消息添加到队列中,然后处理该队列以在每次解除警报时删除警报,但我不确定 id 是如何做到这一点的。

任何想法将不胜感激。 谢谢

【问题讨论】:

【参考方案1】:

您可以在 UIAlertView 委托方法中轻松完成此操作,该方法会在警报视图关闭后调用。所以,UIAlertViewDelegate 定义了如下委托方法:

– alertView:didDismissWithButtonIndex:

实现该方法,并确保您的类是您创建的 UIAlertViews 的委托。此方法是根据用户关闭的警报显示 next 警报的理想场所。

如果您的要求是“按顺序显示最多三个警报,但不总是以相同的顺序显示”我可能会将警报放入数组中,然后在委托方法中从数组中获取下一个警报显示。它实际上不必比这更复杂。关键是委托方法实现是显示下一个警报的最佳位置。

伪代码示例:

定义一个数组; NSMutableArray * alerts_;

- (void)showAlertSequence 
    if ( !alerts_ ) 
        alerts_ = [[NSMutableArray alloc] init];
    

    [alerts_ addObjects;<My alerts>];

    [self showSequencedAlertFrom:nil];


- (BOOL)showSequencedAlertFrom:(UIAlertView *)sourceAlertView 

    if ( !sourceAlertView ) 
        [[alerts_ objectAtIndex:0] show];
    
    else 
        NSInteger index = [alerts_ indexOfObject:sourceAlertView];

        if ( index < [alerts_ count] ) 
            [[alerts_ objectAtIndex:index++] show];
        
    

    return NO;


– alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)index 

    // Show the next alert or clean up if we're at the end of the sequence.
    if ( ![self showSequencedAlertFrom:alertView] ) 
        [alerts_ removeAllObjects];
    
   

顺便说一句;三个连续的警报真的会惹恼你的用户;)

【讨论】:

谢谢@dannywartnaby 这类似于我已经尝试过的东西。问题是在这里你假设所有的 alertViews 都存在。如果只存在 alertView 1 和 3 怎么办?如果是这种情况,您的伪代码将跳过 alert3。还是我遗漏了一些明显的东西? 这种方法的另一个问题是警报 1 不会总是存在,这意味着其他警报都不会显示。这就是为什么我正在考虑对委托方法使用不同的方法。感谢您的回复 好的,我明白了。好吧,我已经稍微更新了我的答案;底线是委托方法是,IMO,决定何时显示下一个警报的最佳位置。如果您不能始终如一地知道正在显示哪些警报以及以什么顺序显示,那么 IMO 将您要显示的警报放入一个集合中,然后在委托方法中迭代该集合,获得“下一个”是有意义的基于当前。 再次感谢!这似乎更符合逻辑。我试图实现这一点,但是当我关闭第一个 alertView 时,控制台返回:wait_fences: failed to receive reply: 10004003 message。应用程序停止。我看不出有什么明显的原因可能导致这种情况。再次感谢您的帮助,我想我快到了!【参考方案2】:

我做过的一件事是通过在 AlertView 上添加类别来使用基于块的 UIAlertViews。

这是.h文件

@interface UIAlertView (WithBlocks)

- (id) initWithTitle:(NSString *)title message:(NSString *)message;
- (void) addButtonWithTitle:(NSString *)title andBlock:(void(^)())block;

@end

这是.m文件

static NSString *BUTTON_BLOCK_KEY = @"alertview-button-blocks";

@interface UIAlertView()
- (void) runBlock: (void (^)())block;
@end

@implementation UIAlertView (WithBlocks)

/**
 * Initialized an alert view with a title and message.
 */
- (id) initWithTitle:(NSString *)title message:(NSString *)message

    self = [self initWithTitle:title message:message delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
    if (self) 
        self.delegate = self;
        NSMutableArray *buttonBlocks = [NSMutableArray array];
        objc_setAssociatedObject(self, BUTTON_BLOCK_KEY, buttonBlocks, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    
    return self;


/**
 * Adds a button with a title and a block to be executed when that button is tapped.
 */
- (void) addButtonWithTitle:(NSString *)title andBlock:(void (^)())block

    // Add the button
    [self addButtonWithTitle:title];
    NSMutableArray *buttonBlocks = objc_getAssociatedObject(self, BUTTON_BLOCK_KEY);
    if (!block) 
        block = ^ /* empty block */ ;
    
    [buttonBlocks addObject:[[[block copy] retain] autorelease]];


- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex

    NSMutableArray *buttonBlocks = objc_getAssociatedObject(self, BUTTON_BLOCK_KEY);
    void (^block)() = (void (^)()) [buttonBlocks objectAtIndex:buttonIndex];

    // Due to a timing issue, the current window is still the UIAlertView for a very
    // short amount of time after it has been dismissed which messes up anything
    // trying to get the current window in the blocks being run.
    // Ergo, the block is being delayed by a tiny bit. (Amount determined through limited testing)
    [self performSelector:@selector(runBlock:) withObject:block afterDelay:0.25];


- (void) runBlock: (void (^)())block

    block();


@end

然后你可以通过下面的代码调用链式的alertviews

 void(^continueBlock)(void) = ^
     // Display more alertviews here
 ;

 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Title" message:@"message"];
 [alert addButtonWithTitle:@"Continue" andBlock:continueBlock];

 [alert addButtonWithTitle:@"Dismiss" andBlock:^
   // Display more alertviews here
 
 [alert show];
 [alert release];

【讨论】:

【参考方案3】:

我也在寻找解决这个问题的方法。这是我最终为自己的应用解决它的方式:

static BOOL alertShowing = FALSE;

UIAlertView *alert0 = [[UIAlertView alloc] initWithTitle:@"AlertView 0" message:@"This is the first alert" delegate:self cancelButtonTitle:nil otherButtonTitles:@"Yes",@"No", nil];
[alert0 setTag:0];
alertShowing = TRUE;
[alert0 show];

while (alertShowing)  
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];


UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:@"AlertView 1" message:@"This is the second alert" delegate:self cancelButtonTitle:nil otherButtonTitles:@"Yes",@"No", nil];
[alert1 setTag:1];
alertShowing = TRUE;
[alert1 show];

while (alertShowing)  
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.2]];


// add some more alerts here for dramatic effect ...

您的按钮处理程序必须在每个退出路径中设置alertShowing = FALSE'

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex 
    // Deal with handling responses for your different alerts here.
    switch ([alertView tag]) 
        case 0:
            // handler first alert here
            break;
        case 1:
            // handler second alert here
            break;
        default:
            // etc.
            break;
    

    alertShowing = FALSE;

可能有比创建一个新的运行循环更好的坐下来旋转的方法,并且有一些重复的代码可能可以更好地通用化。从好的方面来说,它很简单,不需要一堆排队逻辑。我为这种模式使用了#define 来避免手动输入它,并且在我的情况下它运行良好。

【讨论】:

【参考方案4】:

这就是我按照您的建议使用警报队列的方法。

@property (strong, nonatomic) NSMutableArray *alertQueue;
@property (nonatomic) BOOL showingAlert;

- (void)showAlert:(UIAlertView *)alert 
  if (self.showingAlert) 
    [self.alertQueue addObject:alert];
  
  else 
    self.showingAlert = YES;
    [alert show];
  


- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

  if ([self.alertQueue count]) 
    UIAlertView *alert = [self.alertQueue objectAtIndex:0];
    [self.alertQueue removeObjectAtIndex:0];
    [alert show];
  
  else self.showingAlert = NO;

然后,每当您想显示警报时,您只需创建 UIAlertView 并将其传递给 showAlert 方法,它只会在所有早期警报都被解除后才会显示。

【讨论】:

以上是关于按顺序显示 UIAlertViews的主要内容,如果未能解决你的问题,请参考以下文章

按字母顺序显示用户的输入名称

如何在 Xamarin Forms 中按顺序显示(出现)元素

如何在Listview中按字母顺序显示联系人[关闭]

PHP遍历目录下的图片,按顺序显示问题

Linq 首先按特定数字排序,然后按顺序显示所有其余部分

Node.js / Mongoose / 按顺序排序和显示