不在视图控制器中时如何呈现UIAlertController? [英] How to present UIAlertController when not in a view controller?

查看:207
本文介绍了不在视图控制器中时如何呈现UIAlertController?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

场景:用户点击视图控制器上的按钮。视图控制器是导航堆栈中最顶层的(显然)。 tap会调用另一个类上调用的实用程序类方法。在那里发生了一件坏事,我想在控制权返回到视图控制器之前在那里显示警告。

Scenario: The user taps on a button on a view controller. The view controller is the topmost (obviously) in the navigation stack. The tap invokes a utility class method called on another class. A bad thing happens there and I want to display an alert right there before control returns to the view controller.

+ (void)myUtilityMethod {
    // do stuff
    // something bad happened, display an alert.
}

这可以通过 UIAlertView (但也许不太合适)。

This was possible with UIAlertView (but perhaps not quite proper).

在这种情况下,你如何在 myUtilityMethod <中提供 UIAlertController / code>?

In this case, how do you present a UIAlertController, right there in myUtilityMethod?

推荐答案

在WWDC,我在其中一个实验室停下来,向苹果工程师问了同样的问题:什么是最好的练习显示 UIAlertController ?而且他说他们已经得到了很多这个问题,我们开玩笑说他们应该有一个会议。他说内部Apple正在使用透明的 UIViewController 创建 UIWindow ,然后呈现 UIAlertController 就可以了。基本上Dylan Betterman的回答是什么。

At WWDC, I stopped in at one of the labs and asked an Apple Engineer this same question: "What was the best practice for displaying a UIAlertController?" And he said they had been getting this question a lot and we joked that they should have had a session on it. He said that internally Apple is creating a UIWindow with a transparent UIViewController and then presenting the UIAlertController on it. Basically what is in Dylan Betterman's answer.

但是我不想使用 UIAlertController 的子类,因为那样做需要我在整个应用程序中更改我的代码。所以在关联对象的帮助下,我在 UIAlertController 上创建了一个类别,它在Objective-C中提供了一个 show 方法。

But I didn't want to use a subclass of UIAlertController because that would require me changing my code throughout my app. So with the help of an associated object, I made a category on UIAlertController that provides a show method in Objective-C.

以下是相关代码:

#import "UIAlertController+Window.h"
#import <objc/runtime.h>

@interface UIAlertController (Window)

- (void)show;
- (void)show:(BOOL)animated;

@end

@interface UIAlertController (Private)

@property (nonatomic, strong) UIWindow *alertWindow;

@end

@implementation UIAlertController (Private)

@dynamic alertWindow;

- (void)setAlertWindow:(UIWindow *)alertWindow {
    objc_setAssociatedObject(self, @selector(alertWindow), alertWindow, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIWindow *)alertWindow {
    return objc_getAssociatedObject(self, @selector(alertWindow));
}

@end

@implementation UIAlertController (Window)

- (void)show {
    [self show:YES];
}

- (void)show:(BOOL)animated {
    self.alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.alertWindow.rootViewController = [[UIViewController alloc] init];

    id<UIApplicationDelegate> delegate = [UIApplication sharedApplication].delegate;
    // Applications that does not load with UIMainStoryboardFile might not have a window property:
    if ([delegate respondsToSelector:@selector(window)]) {
        // we inherit the main window's tintColor
        self.alertWindow.tintColor = delegate.window.tintColor;
    }

    // window level is above the top window (this makes the alert, if it's a sheet, show over the keyboard)
    UIWindow *topWindow = [UIApplication sharedApplication].windows.lastObject;
    self.alertWindow.windowLevel = topWindow.windowLevel + 1;

    [self.alertWindow makeKeyAndVisible];
    [self.alertWindow.rootViewController presentViewController:self animated:animated completion:nil];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];

    // precaution to insure window gets destroyed
    self.alertWindow.hidden = YES;
    self.alertWindow = nil;
}

@end

以下是一个示例用法:

Here is a sample usage:

// need local variable for TextField to prevent retain cycle of Alert otherwise UIWindow
// would not disappear after the Alert was dismissed
__block UITextField *localTextField;
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Global Alert" message:@"Enter some text" preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
    NSLog(@"do something with text:%@", localTextField.text);
// do NOT use alert.textfields or otherwise reference the alert in the block. Will cause retain cycle
}]];
[alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
    localTextField = textField;
}];
[alert show];

创建的 UIWindow 将被销毁当 UIAlertController 被解除分配时,因为它是唯一保留 UIWindow 的对象。但是,如果您将 UIAlertController 分配给某个属性,或者通过访问其中一个操作块中的警报来使其保留计数增加,则 UIWindow 将保留在屏幕上,锁定你的用户界面。请参阅上面的示例用法代码以避免在需要访问 UITextField 的情况下。

The UIWindow that is created will be destroyed when the UIAlertController is dealloced, since it is the only object that is retaining the UIWindow. But if you assign the UIAlertController to a property or cause its retain count to increase by accessing the alert in one of the action blocks, the UIWindow will stay on screen, locking up your UI. See the sample usage code above to avoid in the case of needing to access UITextField.

我做了一个GitHub repo测试项目: FFGlobalAlertController

I made a GitHub repo with a test project: FFGlobalAlertController

这篇关于不在视图控制器中时如何呈现UIAlertController?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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