并发UIAlertControllers [英] Concurrent UIAlertControllers

查看:110
本文介绍了并发UIAlertControllers的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



所以我改变了使用UIAlertController的东西。这在大多数情况下都适用。



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



例如..警告,你没有设置X并且在完成项目之前需要做Y和警告,你使用的是测试版并且不依赖于结果等。 (这些只是示例!)



在UIAlertView下,我会(比如说)同时获得两个警告框,用户必须点击两次以解除两个警告框...但它们都出现了。



在UIAlertController下,下面的代码显示一般警报,我只收到一条警报消息和一条控制台消息:



警告:尝试在TestViewController上呈现UIAlertController:0x13f667bb0:0x13f63cb40已经呈现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)
{
/ /在这里做一些事情
[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 ???

解决方案

我完全了解这里的问题,并通过一个UIAlertController类别提出了以下解决方案。它的设计使得如果已经出现警报,它会延迟显示下一个警报,直到它收到第一个已被解除的通知为止。



UIAlertController + MH.h

  #import< UIKit / UIKit.h> 

@interface UIAlertController(MH)

//提供UIAlertView的先前行为,因为警报排队等候。
- (void)mh_show;

@end

UIAlertController + MH.m

  @implementation UIAlertController(MH)

//通过swizzling替换viewDidDisappear的实现。
+(void)load {
static dispatch_once_t once_token;
dispatch_once(& once_token,^ {
方法originalMethod = class_getInstanceMethod(self,@ selector(viewDidDisappear :));
方法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];
//我们需要保留窗口,以便在解除分配之前将其设置为隐藏,以便观察点火。
[self mh_setAlertWindow:w];
w.rootViewController = [[UIViewController alloc] init];
w.windowLevel = UIWindowLevelAlert;
[w makeKeyAndVisible];
[w.rootViewController presentViewController:self animated:YES completion:nil];
};

//检查现有密钥窗口是否已显示警报。它可能是我们的窗口或UIAlertView的窗口。
UIWindow * keyWindow = [UIApplication sharedApplication] .keyWindow;
if(keyWindow.windowLevel == UIWindowLevelAlert){
//如果是,则延迟显示此新警报,直到前一个被解除。
__block id observer;
observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification * _Nonnull note){
[[NSNotificationCenter defaultCenter] removeObserver:observer];
showAlert();
}];
} else {
//否则立即显示警报。
showAlert();
}
}

- (void)mh_viewDidDisappear:(BOOL)动画{
[self mh_viewDidDisappear:animated]; //调用原始实现
[self mh_alertWindow] .hidden = YES;
}

@end

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



要测试这一点,你需要做的就是在有两个不同的警报控制器的行,你会看到第二个等待,直到第一个被解雇,然后被呈现。


I'm porting my app to iOS 8.0 and notice that UIAlertView is deprecated.

So I've changed things to use a UIAlertController. Which works in most circumstances.

Except, when my app opens, it does several checks to report various states back to the user...

E.g... "Warning, you haven't set X up and need to do Y before completing projects" and "Warning, you are using a beta version and do not rely on results" etc...(these are just examples!)

Under the UIAlertView, I would (say) get two alert boxes concurrently which the user has to tap twice to dismiss both...but they both appear.

Under UIAlertController with the code below to present a 'general' alert, I only get one alert message along with a console message:

Warning: Attempt to present UIAlertController: 0x13f667bb0 on TestViewController: 0x13f63cb40 which is already presenting UIAlertController: 0x13f54edf0

So, although the above probably isn't a good example, I'm thinking there may be times when more than one global alert may need to be presented due to 'events' whilst operating an app. Under the old UIAlertView, they would appear but it seems they will not under a UIAlertController.

Can anyone suggest how this could be achieved with a UIAlertController?

Thanks

+(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];

Edit: I notice that on iOS8, presenting two AlertViews consecutively, they are 'queued' and appear sequentially whereas in iOS7, they appear concurrently. It seems Apple have altered UIAlertView to queue multiple instances. Is there a way to do this with UIAlertController without continuing to use the (deprecated but modified) UIAlertView???

解决方案

I fully understand the issue here and came up with the following solution via a category of UIAlertController. It's designed so that if an alert is already being presented, it delays showing of the next alert until it receives a notification that the first has been dismissed.

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

This code even handles the case where a previous alert was presented via the deprecated UIAlertView, i.e. it waits on it to finish too.

To test this out all you need to do is call show twice in a row with two different alert controllers and you will see the second one waits until the first has been dismissed before being presented.

这篇关于并发UIAlertControllers的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆