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

查看:29
本文介绍了不在视图控制器中时如何呈现 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?

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

推荐答案

在 WWDC,我在其中一个实验室停下来问了一位 Apple 工程师同样的问题:显示 的最佳做法是什么?UIAlertController?"他说他们经常收到这个问题,我们开玩笑说他们应该就这个问题进行一次会议.他说,Apple 在内部创建了一个带有透明 UIViewControllerUIWindow,然后在其上呈现 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 ensure window gets destroyed
    self.alertWindow.hidden = YES;
    self.alertWindow = nil;
}

@end

这是一个示例用法:

// 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];

UIAlertController 被释放时,创建的 UIWindow 将被销毁,因为它是唯一保留 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 存储库:FFGlobalAlertController

I made a GitHub repo with a test project: FFGlobalAlertController

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

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