【问题标题】:Concurrent UIAlertControllers并发 UIAlertControllers
【发布时间】:2014-11-13 23:16:52
【问题描述】:

我正在将我的应用程序移植到 iOS 8.0 并注意到 UIAlertView 已被弃用。

所以我改变了一些东西来使用 UIAlertController。在大多数情况下都有效。

除了,当我的应用打开时,它会进行多次检查以向用户报告各种状态...

例如..“警告,您尚未设置 X,需要在完成项目之前进行 Y”和“警告,您使用的是 beta 版本,不依赖结果”等...(这些只是例子!)

在 UIAlertView 下,我会(比如说)同时获得两个警报框,用户必须点击两次才能关闭这两个框...但它们都会出现。

在 UIAlertController 下,使用以下代码显示“常规”警报,我只收到一条警报消息和一条控制台消息:

警告:尝试在 TestViewController: 0x13f63cb40 上呈现 UIAlertController: 0x13f667bb0,它已经呈现 UIAlertController: 0x13f54edf0

因此,尽管上述示例可能不是一个很好的示例,但我认为有时可能由于操作应用程序时的“事件”而需要呈现多个全局警报。在旧的 UIAlertView 下,它们会出现,但似乎不会出现在 UIAlertController 下。

谁能建议如何使用 UIAlertController 来实现这一点?

谢谢

+(void)presentAlert:(NSString*)alertMessage withTitle:(NSString*)title
{
    UIAlertController *alertView = [UIAlertController
                                    alertControllerWithTitle:title
                                    message:alertMessage
                                    preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction* ok = [UIAlertAction
                         actionWithTitle:kOkButtonTitle
                         style:UIAlertActionStyleDefault
                         handler:^(UIAlertAction * action)
                         {
                             //Do some thing here
                             [alertView dismissViewControllerAnimated:YES completion:nil];
                         }];

    [alertView addAction:ok];

    UIViewController *rootViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
    [rootViewController presentViewController:alertView animated:YES completion:nil];

编辑:我注意到在 iOS8 上,连续显示两个 AlertView,它们是“排队”并按顺序出现,而在 iOS7 中,它们是同时出现的。似乎 Apple 已更改 UIAlertView 以对多个实例进行排队。有没有办法用 UIAlertController 做到这一点,而无需继续使用(已弃用但已修改)的 UIAlertView ???

【问题讨论】:

  • 您是否有幸找到问题的答案?
  • 遗憾的是没有。目前,我坚持使用明显修改的 UIAlertView。时间限制使我无法进一步调查。
  • 我们有解决这个问题的办法吗?
  • 我刚刚发布了我的解决方案,请在下面查看。
  • UIAlertController 在我看来越来越像一个不到半美元的工作。

标签: ipad ios8 uialertview uialertcontroller


【解决方案1】:

在展示 UIAlertController 时,我也遇到了一些问题。 现在我可以建议的唯一解决方案是从最上面的presentedViewContrller(如果有)或窗口的rootViewController 中显示警报控制器。

UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;

while(presentingViewController.presentedViewController != nil)
{
    presentingViewController = presentingViewController.presentedViewController;
}

[presentingViewController presentViewController:alertView animated:YES completion:nil];

您收到的警告不仅限于 UIAlertController。视图控制器(在您的情况下为窗口的 rootViewController)一次只能显示一个视图控制器。

【讨论】:

  • 这消除了警告,但警报相互叠加。有什么我想念的吗
  • 这是使用 UIAlertController 及以上解决方案的预期行为。使用 UIAlertView iOS 正在管理显示/堆叠警报(在显示新警报时隐藏现有警报,然后在解除新警报时显示先前的警报)。如果您不想在出现新警报时看到之前的警报,则必须关闭之前的警报。
  • 我正在考虑解决这个问题。我不确定它是否会起作用,请尝试让我们知道。您可以创建自定义 UIAlertController 类。实现 viewWillAppear 和 viewWillDisappear(不确定在呈现新警报时是否会为先前的警报调用此方法)并反转 view.alpha。
【解决方案2】:

我完全理解这里的问题,并通过 UIAlertController 类别提出了以下解决方案。其设计目的是,如果已显示警报,则会延迟显示下一个警报,直到收到第一个警报已被取消的通知。

UIAlertController+MH.h

#import <UIKit/UIKit.h>

@interface UIAlertController (MH)

// Gives previous behavior of UIAlertView in that alerts are queued up.
-(void)mh_show;

@end

UIAlertController+MH.m

@implementation UIAlertController (MH)

// replace the implementation of viewDidDisappear via swizzling.
+ (void)load {
    static dispatch_once_t once_token;
    dispatch_once(&once_token,  ^{
        Method originalMethod = class_getInstanceMethod(self, @selector(viewDidDisappear:));
        Method extendedMethod = class_getInstanceMethod(self, @selector(mh_viewDidDisappear:));
        method_exchangeImplementations(originalMethod, extendedMethod);
    });
}

-(UIWindow*)mh_alertWindow{
    return objc_getAssociatedObject(self, "mh_alertWindow");
}

-(void)mh_setAlertWindow:(UIWindow*)window{
    objc_setAssociatedObject(self, "mh_alertWindow", window, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(void)mh_show{
    void (^showAlert)() = ^void() {
        UIWindow* w = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
        // we need to retain the window so it can be set to hidden before it is dealloced so the observation fires.
        [self mh_setAlertWindow:w];
        w.rootViewController = [[UIViewController alloc] init];
        w.windowLevel = UIWindowLevelAlert;
        [w makeKeyAndVisible];
        [w.rootViewController presentViewController:self animated:YES completion:nil];
    };

    // check if existing key window is an alert already being shown. It could be our window or a UIAlertView's window.
    UIWindow* keyWindow = [UIApplication sharedApplication].keyWindow;
    if(keyWindow.windowLevel == UIWindowLevelAlert){
        // if it is, then delay showing this new alert until the previous has been dismissed.
        __block id observer;
        observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification * _Nonnull note) {
            [[NSNotificationCenter defaultCenter] removeObserver:observer];
            showAlert();
        }];
    }else{
        // otherwise show the alert immediately.
        showAlert();
    }
}

- (void)mh_viewDidDisappear:(BOOL)animated {
    [self mh_viewDidDisappear:animated]; // calls the original implementation
    [self mh_alertWindow].hidden = YES;
}

@end

此代码甚至可以处理通过已弃用的 UIAlertView 呈现先前警报的情况,即它也等待它完成。

要对此进行测试,您需要做的就是使用两个不同的警报控制器连续调用 show 两次,您会看到第二个等待直到第一个被关闭后再呈现。

【讨论】:

  • [super viewDidDisappear:] 在你的代码中不会像你想的那样做。它不会调用-[UIAlertControl viewDidDisappear:],而是直接超类之一-[UIViewController viewDidDisappear:]。由于您不知道您省略了 -[UIAlertController viewDidDisappear:] 的实现中的内容,因此这不是“危险的”,这是一个错误。有一种 hacky 技术可以从类别中的覆盖访问方法的原始实现(搜索方法调配),但这确实是大材小用 - 使用子类方法。
  • 感谢文档对此模棱两可,甚至认为我已经对其进行了测试,但一定是弄错了。我更新了代码以调整方法并通过子类进行测试,它正在工作。我将坚持使用该类别,因为我相信我最终会弄清楚如何在没有调酒或保留窗口的情况下实现这一目标。
  • 根据黄金词法 swizzling 和您的 SO 分数看起来不错...但是有人成功使用它了吗?安全吗?当我去做其他事情时,我可以把我的孩子放在它前面吗?
  • 只是我在想的另一种方法... UIViewController 的子类,添加 -(void)showUialertController:(UIAlertController*)alert; 或类似名称,它在引擎盖下执行排队。不确定我们是否必须关心 UIAlertViews。
  • 虽然有时您希望显示来自视图控制器以外的类的警报。
【解决方案3】:

这个解决方案对我有用。我有一个 AlertManager 正在处理一个接一个出现的警报队列。为了知道何时显示另一个警报,我扩展了 UIAlertController 并覆盖了它的 viewDidDisappear 函数。

这个解决方案必须在 viewDidAppear 之后使用。如果不是,则不会显示警报。链条将被破坏,并且不会出现进一步的警报。另一种选择是稍后尝试挂起的警报或将其丢弃,这将为将来的警报释放队列。

/// This class presents one alert after another.
/// - Attention:  If one of the alerts are not presented for some reason (ex. before viewDidAppear), it will not disappear either and the chain will be broken. No further alerts would be shown.
class AlertHandler {
    private var alertQueue = [UIAlertController]()
    private var alertInProcess: UIAlertController?

    // singleton
    static let sharedAlerts = AlertHandler()
    private init() {}

    func addToQueue(alert: UIAlertController) {
        alertQueue.append(alert)
        handleQueueAdditions()
    }

    private func handleQueueAdditions() {
        if alertInProcess == nil {
            let alert = alertQueue.removeFirst()
            alertInProcess = alert
            UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
        }
    }

    private func checkForNextAlert(alert: UIAlertController) {
        if alert === alertInProcess {
            if alertQueue.count > 0 {
                let alert = alertQueue.removeFirst()
                alertInProcess = alert
                UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
            } else {
                alertInProcess = nil
            }
        }
    }
}

extension UIAlertController {
    public override func viewDidDisappear(animated: Bool) {
        AlertHandler.sharedAlerts.checkForNextAlert(self)
    }
}

AlertHandler.sharedAlerts.addToQueue(alert:)

【讨论】:

    【解决方案4】:

    我对这里的任何解决方案都不满意,因为它们需要过多的手动工作或需要调配,而我在生产应用程序中对此并不满意。我创建了一个新类 (GitHub),它从此处的其他答案中获取元素。

    AlertQueue.h

    //
    //  AlertQueue.h
    //
    //  Created by Nick Brook on 03/02/2017.
    //  Copyright © 2018 Nick Brook. All rights reserved.
    //
    
    #import <UIKit/UIKit.h>
    
    @protocol AlertQueueAlertControllerDelegate;
    
    @interface AlertQueueAlertController : UIAlertController
    
    /**
     The alert delegate
     */
    @property(nonatomic, weak, nullable) id<AlertQueueAlertControllerDelegate> delegate;
    
    /**
     Any relevant user info for this alert
     */
    @property(nonatomic, readonly, nullable) NSDictionary * userInfo;
    
    /**
     The view controller that requested the alert be displayed, if one was passed when adding to the queue
     */
    @property(nonatomic, weak, readonly, nullable) UIViewController *presentingController;
    
    /**
     Create an alert with a title, message and user info
    
     @param title The title for the alert
     @param message The message for the alert
     @param userInfo The user info dictionary
     @return An alert
     */
    + (nonnull instancetype)alertControllerWithTitle:(nullable NSString *)title message:(nullable NSString *)message userInfo:(nullable NSDictionary *)userInfo;
    
    /**
     - Warning: This method is not available on this subclass. Use +alertControllerWithTitle:message:userInfo: instead.
     */
    + (nonnull instancetype)alertControllerWithTitle:(nullable NSString *)title message:(nullable NSString *)message preferredStyle:(UIAlertControllerStyle)preferredStyle NS_UNAVAILABLE;
    
    @end
    
    @interface AlertQueue : NSObject
    
    /**
     The queue of alerts including the currently displayed alerts. The current alert is at index 0 and the next alert to be displayed is at 1. Alerts are displayed on a FIFO basis.
     */
    @property(nonatomic, readonly, nonnull) NSArray<AlertQueueAlertController *> *queuedAlerts;
    
    /**
     The currently displayed alert
     */
    @property(nonatomic, readonly, nullable) AlertQueueAlertController *displayedAlert;
    
    + (nonnull instancetype)sharedQueue;
    
    /**
     Display an alert, or add to queue if an alert is currently displayed
    
     @param alert The alert to display
     */
    - (void)displayAlert:(nonnull AlertQueueAlertController *)alert;
    
    /**
     Display an alert, or add to queue if an alert is currently displayed
    
     @param alert The alert to display
     @param userInfo Any relevant information related to the alert for later reference. If a userinfo dictionary already exists on the alert, the dictionaries will be merged with the userinfo here taking precedence on conflicting keys.
     */
    - (void)displayAlert:(nonnull AlertQueueAlertController *)alert userInfo:(nullable NSDictionary *)userInfo;
    
    /**
     Display an alert, or add to queue if an alert is currently displayed
    
     @param alert The alert to display
     @param viewController The presenting view controller, stored on the alert for future reference
     @param userInfo Any relevant information related to the alert for later reference. If a userinfo dictionary already exists on the alert, the dictionaries will be merged with the userinfo here taking precedence on conflicting keys.
     */
    - (void)displayAlert:(nonnull AlertQueueAlertController *)alert fromController:(nullable UIViewController *)viewController userInfo:(nullable NSDictionary *)userInfo;
    
    /**
     Cancel a displayed or queued alert
    
     @param alert The alert to cancel
     */
    - (void)cancelAlert:(nonnull AlertQueueAlertController *)alert;
    
    /**
     Cancel all alerts from a specific view controller, useful if the controller is dimissed.
    
     @param controller The controller to cancel alerts from
     */
    - (void)invalidateAllAlertsFromController:(nonnull UIViewController *)controller;
    
    @end
    
    @protocol AlertQueueAlertControllerDelegate <NSObject>
    
    /**
     The alert was displayed
    
     @param alertItem The alert displayed
     */
    - (void)alertDisplayed:(nonnull AlertQueueAlertController *)alertItem;
    
    /**
     The alert was dismissed
    
     @param alertItem The alert dismissed
     */
    - (void)alertDismissed:(nonnull AlertQueueAlertController *)alertItem;
    
    @end
    

    AlertQueue.m

    //
    //  AlertQueue.m
    //  Nick Brook
    //
    //  Created by Nick Brook on 03/02/2017.
    //  Copyright © 2018 Nick Brook. All rights reserved.
    //
    
    #import "AlertQueue.h"
    
    @protocol AlertQueueAlertControllerInternalDelegate
    @required
    - (void)alertQueueAlertControllerDidDismiss:(AlertQueueAlertController *)alert;
    
    @end
    
    @interface AlertQueueAlertController()
    
    @property(nonatomic, strong, nullable) NSDictionary * userInfo;
    @property (nonatomic, weak, nullable) id<AlertQueueAlertControllerInternalDelegate> internalDelegate;
    @property(nonatomic, weak) UIViewController *presentingController;
    
    @end
    
    @implementation AlertQueueAlertController
    
    + (instancetype)alertControllerWithTitle:(NSString *)title message:(NSString *)message userInfo:(NSDictionary *)userInfo {
        AlertQueueAlertController *ac = [super alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
        ac.userInfo = userInfo;
        return ac;
    }
    
    - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
        [super dismissViewControllerAnimated:flag completion:completion];
    }
    
    - (void)viewWillDisappear:(BOOL)animated {
        [super viewWillDisappear:animated];
    }
    
    - (void)viewDidDisappear:(BOOL)animated {
        [super viewDidDisappear:animated];
        [self.internalDelegate alertQueueAlertControllerDidDismiss:self];
    }
    
    @end
    
    @interface AlertQueue() <AlertQueueAlertControllerInternalDelegate>
    
    @property(nonatomic, strong, nonnull) NSMutableArray<AlertQueueAlertController *> *internalQueuedAlerts;
    @property(nonatomic, strong, nullable) AlertQueueAlertController *displayedAlert;
    @property(nonatomic, strong) UIWindow *window;
    @property(nonatomic, strong) UIWindow *previousKeyWindow;
    
    @end
    
    @implementation AlertQueue
    
    + (nonnull instancetype)sharedQueue {
        static AlertQueue *sharedQueue = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            sharedQueue = [AlertQueue new];
        });
        return sharedQueue;
    }
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            self.window = [UIWindow new];
            self.window.windowLevel = UIWindowLevelAlert;
            self.window.backgroundColor = nil;
            self.window.opaque = NO;
            UIViewController *rvc = [UIViewController new];
            rvc.view.backgroundColor = nil;
            rvc.view.opaque = NO;
            self.window.rootViewController = rvc;
            self.internalQueuedAlerts = [NSMutableArray arrayWithCapacity:1];
            [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeHidden:) name:UIWindowDidBecomeHiddenNotification object:nil];
        }
        return self;
    }
    
    - (void)dealloc {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
    }
    
    - (void)windowDidBecomeHidden:(NSNotification *)notification {
        [self displayAlertIfPossible];
    }
    
    - (void)alertQueueAlertControllerDidDismiss:(AlertQueueAlertController *)alert {
        if(self.displayedAlert != alert) { return; }
        self.displayedAlert = nil;
        [self.internalQueuedAlerts removeObjectAtIndex:0];
        if([alert.delegate respondsToSelector:@selector(alertDismissed:)]) {
            [alert.delegate alertDismissed:(AlertQueueAlertController * _Nonnull)alert];
        }
        [self displayAlertIfPossible];
    }
    
    - (void)displayAlertIfPossible {
        UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
        if(self.displayedAlert != nil || (keyWindow != self.window && keyWindow.windowLevel >= UIWindowLevelAlert)) {
            return;
        }
        if(self.internalQueuedAlerts.count == 0) {
            self.window.hidden = YES;
            [self.previousKeyWindow makeKeyWindow];
            self.previousKeyWindow = nil;
            return;
        }
        self.displayedAlert = self.internalQueuedAlerts[0];
        self.window.frame = [UIScreen mainScreen].bounds;
        if(!self.window.isKeyWindow) {
            self.previousKeyWindow = UIApplication.sharedApplication.keyWindow;
            [self.window makeKeyAndVisible];
        }
        [self.window.rootViewController presentViewController:(UIViewController * _Nonnull)self.displayedAlert animated:YES completion:nil];
        if([self.displayedAlert.delegate respondsToSelector:@selector(alertDisplayed:)]) {
            [self.displayedAlert.delegate alertDisplayed:(AlertQueueAlertController * _Nonnull)self.displayedAlert];
        }
    }
    
    - (void)displayAlert:(AlertQueueAlertController *)alert {
        [self displayAlert:alert userInfo:nil];
    }
    
    - (void)displayAlert:(AlertQueueAlertController *)alert userInfo:(NSDictionary *)userInfo {
        [self displayAlert:alert fromController:nil userInfo:userInfo];
    }
    
    - (void)displayAlert:(AlertQueueAlertController *)alert fromController:(UIViewController *)viewController userInfo:(NSDictionary *)userInfo {
        if(alert.preferredStyle != UIAlertControllerStyleAlert) { // cannot display action sheets
            return;
        }
        alert.internalDelegate = self;
        if(userInfo) {
            if(alert.userInfo) {
                NSMutableDictionary *d = alert.userInfo.mutableCopy;
                [d setValuesForKeysWithDictionary:userInfo];
                alert.userInfo = d;
            } else {
                alert.userInfo = userInfo;
            }
        }
        alert.presentingController = viewController;
        [self.internalQueuedAlerts addObject:alert];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self displayAlertIfPossible];
        });
    }
    
    - (void)cancelAlert:(AlertQueueAlertController *)alert {
        if(alert == self.displayedAlert) {
            [self.displayedAlert dismissViewControllerAnimated:YES completion:nil];
        } else {
            [self.internalQueuedAlerts removeObject:alert];
        }
    }
    
    - (void)invalidateAllAlertsFromController:(UIViewController *)controller {
        NSArray<AlertQueueAlertController *> *queuedAlerts = [self.internalQueuedAlerts copy];
        for(AlertQueueAlertController *alert in queuedAlerts) {
            if(alert.presentingController == controller) {
                [self cancelAlert:alert];
            }
        }
    }
    
    - (NSArray<AlertQueueAlertController *> *)queuedAlerts {
        // returns new array so original can be manipulated (alerts cancelled) while enumerating
        return [NSArray arrayWithArray:_internalQueuedAlerts];
    }
    
    @end
    

    示例用法

    AlertQueueAlertController *ac = [AlertQueueAlertController alertControllerWithTitle:@"Test1" message:@"Test1" userInfo:nil];
    [ac addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSLog(@"Alert!");
    }]];
    [[AlertQueue sharedQueue] displayAlert:ac fromController:self userInfo:nil];
    

    【讨论】:

    • 不错的解决方案,但由于某种原因对我来说操作不起作用。
    • 如果您还没有,请尝试 github 版本。我做了一些修复和改进。
    【解决方案5】:

    这可以通过在 UIAlertcontroller 的操作处理程序中使用检查标志来解决。

    - (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    _isShowAlertAgain = YES;
    [self showAlert];
    }
    
    - (void)showAlert {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:@"This is Alert" preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
        [alertController dismissViewControllerAnimated:YES completion:nil];
        if (_isShowAlertAgain) {
            _isShowAlertAgain = NO;
            [self showAlert];
        }
    }];
    UIAlertAction *cancelButton = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        [alertController dismissViewControllerAnimated:YES completion:nil];
    }];
    [alertController addAction:okButton];
    [alertController addAction:cancelButton];
    [self presentViewController:alertController animated:YES completion:nil];
    }
    

    【讨论】:

      【解决方案6】:

      我创建了一个 Github 项目 MAAlertPresenter 并带有一个演示来处理这个问题。只需几行更改,您就可以使用它一一呈现 UIAlertController。

      【讨论】:

        【解决方案7】:

        这似乎是一个老问题,但仍然发布为它可能对寻找这个的人有用,尽管 Apple 不建议堆叠多个警报,这就是他们弃用 UIAlertView 到 UIAlertController 实现的原因。

        我为 UIAlertAction 创建了 AQAlertAction 子类。您可以将它用于交错警报,用法与您使用 UIAlertAction 相同。您需要做的就是在您的项目中导入AQMutiAlertFramework,或者您也可以包含类(请参阅Sample project)。 在内部,它使用二进制信号量来错开警报,直到用户处理与显示当前警报相关的操作。让我知道它是否适合您。

        【讨论】:

        • 这可能会被标记为垃圾邮件,除非您在答案中添加一些解释,而不是仅仅要求人们使用您的代码。
        • 我想我已经解释过了。它所做的一切都是在二进制信号量队列中不断添加警报,直到用户处理该操作,这导致将信号分派到信号量。如有混淆请参考代码。它的实现非常小。
        【解决方案8】:

        从 UIAlertView 切换到 UIAlertController 后,我也遇到了同样的问题。我不喜欢 Apple 的政策,因为“消息框”在 BIG BANG 的几乎所有 SO 中一直是可堆叠的。 我同意并发警报并不是一个很好的用户体验,有时它是糟糕设计的结果,但有时(例如 UILocalNotification 或类似的东西)它们可能会发生,而且我害怕我会因为我的应用程序而失去一个重要的阻塞警报刚刚收到通知。

        也就是说,这是我的 2cents 解决方案,一个递归函数,如果发件人没有presentedViewController,它会尝试在发送者上呈现警报控制器,否则它会尝试在presentedViewController 上呈现警报控制器等等......它没有如果您同时触发更多 AlertController 则可以工作,因为您无法从正在显示的控制器中显示视图控制器,但它应该可以在任何其他合理的工作流程中工作。

        + (void)presentAlert:(UIAlertController *)alert withSender:(id)sender
        {
            if ([sender presentedViewController])
            {
                [self presentAlert:alert withSender: [sender presentedViewController]];
            }
            else
            {
                [sender presentViewController:alert animated:YES completion:nil];
            }
        }
        

        【讨论】:

          【解决方案9】:

          如果您需要的只是简单的信息警报,可以简单地读取和关闭,那么这就是我刚刚想出的(它并不完全是花哨的高级代码,并且存在“耦合”涉及,但是,嘿...它很短/简单,在某些情况下可能有用):

          ReadOnlyMessageQueue.swift:

          import Foundation
          
          protocol ReadOnlyMessageQueueDelegate: class {
              func showAlert(message: String, title: String)
          }
          
          class ReadOnlyMessageQueue {
          
              weak var delegate: ReadOnlyMessageQueueDelegate?
          
              private var queue = [(message: String, title: String)]()
          
              public func addAlertMessageToQueue(message: String, title: String) {
                  print("MQ.add: \(message)")
                  queue.append((message,title))
                  if queue.count == 1 {
                      delegate?.showAlert(message: message, title: title)
                  }
              }
          
              public func alertWasDismissedInParentVC() {
                  print("MQ.wasDissmissed")
                  if queue.count > 1 {
                      delegate?.showAlert(message: queue[1].message, title: self.queue[1].title)
                      self.queue.remove(at: 0)
                  } else if queue.count == 1 {
                      self.queue.remove(at: 0)
                  }
              }
          
          }
          

          ViewController.swift:

          import UIKit
          
          class ViewController: UIViewController, ReadOnlyMessageQueueDelegate {
          
              let messageQueue = ReadOnlyMessageQueue()
          
              override func viewDidLoad() {
                  super.viewDidLoad()
                  messageQueue.delegate = self
              }
          
              override func viewDidAppear(_ animated: Bool) {
                  for i in 4...20 {
                      print("VC.adding: \(i)")
                      messageQueue.addAlertMessageToQueue(message: String(i), title: String(i))
                  }
              }
          
              func showAlert(message: String, title: String) {
                  print("VC.showing: \(message)")
                  let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
                  alert.addAction(UIAlertAction(title: "OK", style: .default, handler: {
                      _ in
                      self.messageQueue.alertWasDismissedInParentVC()
                      }
                  ))
                  self.present(alert, animated: false)
              }
          
          }
          

          【讨论】:

            【解决方案10】:

            我用这行代码解决了这个问题:

            alert.modalTransitionStyle=UIModalPresentationOverCurrentContext;
            

            【讨论】:

            • 您的作业中的类型不兼容。这是更正后的 Swift 样式:alert.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-12-05
            • 2010-10-31
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多