场景:用户点击视图控制器上的按钮。视图控制器是导航堆栈中最顶层的(很明显)。tap调用在另一个类上调用的实用程序类方法。这里发生了不好的事情我想在控件返回到视图控制器之前在那里显示一个警告。
+ (void)myUtilityMethod {
// do stuff
// something bad happened, display an alert.
}
这是可能的UIAlertView(但可能不太合适)。
在这种情况下,你如何在myUtilityMethod中呈现UIAlertController ?
创建helper类AlertWindow并使用as
let alertWindow = AlertWindow();
let alert = UIAlertController(title: "Hello", message: "message", preferredStyle: .alert);
let cancel = UIAlertAction(title: "Ok", style: .cancel){(action) in
//.... action code here
// reference to alertWindow retain it. Every action must have this at end
alertWindow.isHidden = true;
// here AlertWindow.deinit{ }
}
alert.addAction(cancel);
alertWindow.present(alert, animated: true, completion: nil)
class AlertWindow:UIWindow{
convenience init(){
self.init(frame:UIScreen.main.bounds);
}
override init(frame: CGRect) {
super.init(frame: frame);
if let color = UIApplication.shared.delegate?.window??.tintColor {
tintColor = color;
}
rootViewController = UIViewController()
windowLevel = UIWindowLevelAlert + 1;
makeKeyAndVisible()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit{
// semaphor.signal();
}
func present(_ ctrl:UIViewController, animated:Bool, completion: (()->Void)?){
rootViewController!.present(ctrl, animated: animated, completion: completion);
}
}
@agilityvision的答案翻译为Swift4/iOS11。我没有使用本地化字符串,但你可以很容易地改变:
import UIKit
/** An alert controller that can be called without a view controller.
Creates a blank view controller and presents itself over that
**/
class AlertPlusViewController: UIAlertController {
private var alertWindow: UIWindow?
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.alertWindow?.isHidden = true
alertWindow = nil
}
func show() {
self.showAnimated(animated: true)
}
func showAnimated(animated _: Bool) {
let blankViewController = UIViewController()
blankViewController.view.backgroundColor = UIColor.clear
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = blankViewController
window.backgroundColor = UIColor.clear
window.windowLevel = UIWindowLevelAlert + 1
window.makeKeyAndVisible()
self.alertWindow = window
blankViewController.present(self, animated: true, completion: nil)
}
func presentOkayAlertWithTitle(title: String?, message: String?) {
let alertController = AlertPlusViewController(title: title, message: message, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Ok", style: .default, handler: nil)
alertController.addAction(okayAction)
alertController.show()
}
func presentOkayAlertWithError(error: NSError?) {
let title = "Error"
let message = error?.localizedDescription
presentOkayAlertWithTitle(title: title, message: message)
}
}
创建扩展像在Aviel Gross回答。这里有Objective-C扩展。
这里有头文件*.h
// UIAlertController+Showable.h
#import <UIKit/UIKit.h>
@interface UIAlertController (Showable)
- (void)show;
- (void)presentAnimated:(BOOL)animated
completion:(void (^)(void))completion;
- (void)presentFromController:(UIViewController *)viewController
animated:(BOOL)animated
completion:(void (^)(void))completion;
@end
和实现:*.m
// UIAlertController+Showable.m
#import "UIAlertController+Showable.h"
@implementation UIAlertController (Showable)
- (void)show
{
[self presentAnimated:YES completion:nil];
}
- (void)presentAnimated:(BOOL)animated
completion:(void (^)(void))completion
{
UIViewController *rootVC = [UIApplication sharedApplication].keyWindow.rootViewController;
if (rootVC != nil) {
[self presentFromController:rootVC animated:animated completion:completion];
}
}
- (void)presentFromController:(UIViewController *)viewController
animated:(BOOL)animated
completion:(void (^)(void))completion
{
if ([viewController isKindOfClass:[UINavigationController class]]) {
UIViewController *visibleVC = ((UINavigationController *)viewController).visibleViewController;
[self presentFromController:visibleVC animated:animated completion:completion];
} else if ([viewController isKindOfClass:[UITabBarController class]]) {
UIViewController *selectedVC = ((UITabBarController *)viewController).selectedViewController;
[self presentFromController:selectedVC animated:animated completion:completion];
} else {
[viewController presentViewController:self animated:animated completion:completion];
}
}
@end
你在你的实现文件中使用这个扩展,就像这样:
#import "UIAlertController+Showable.h"
UIAlertController* alert = [UIAlertController
alertControllerWithTitle:@"Title here"
message:@"Detail message here"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction
actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
// Add more actions if needed
[alert show];
对于iOS 13,基于mythicalcoder和bobbyrehm的回答:
在iOS 13中,如果你正在创建自己的窗口来显示警报,你需要保持对该窗口的强引用,否则你的警报将不会显示,因为当它的引用退出作用域时,窗口将立即被释放。
此外,在警报解除后,您需要再次将引用设置为nil,以便删除窗口,继续允许用户在它下面的主窗口上进行交互。
你可以创建一个UIViewController子类来封装窗口内存管理逻辑:
class WindowAlertPresentationController: UIViewController {
// MARK: - Properties
private lazy var window: UIWindow? = UIWindow(frame: UIScreen.main.bounds)
private let alert: UIAlertController
// MARK: - Initialization
init(alert: UIAlertController) {
self.alert = alert
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("This initializer is not supported")
}
// MARK: - Presentation
func present(animated: Bool, completion: (() -> Void)?) {
window?.rootViewController = self
window?.windowLevel = UIWindow.Level.alert + 1
window?.makeKeyAndVisible()
present(alert, animated: animated, completion: completion)
}
// MARK: - Overrides
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag) {
self.window = nil
completion?()
}
}
}
你可以这样使用它,或者如果你想在你的UIAlertController上使用一个方便的方法,你可以把它扔到一个扩展中:
extension UIAlertController {
func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
windowAlertPresentationController.present(animated: animated, completion: completion)
}
}
我尝试了上面提到的所有方法,但都没有成功。我在Swift 3.0中使用的方法:
extension UIAlertController {
func show() {
present(animated: true, completion: nil)
}
func present(animated: Bool, completion: (() -> Void)?) {
if var topController = UIApplication.shared.keyWindow?.rootViewController {
while let presentedViewController = topController.presentedViewController {
topController = presentedViewController
}
topController.present(self, animated: animated, completion: completion)
}
}
}