뷰 컨트롤러에 없을 때 UIAlertController를 표시하는 방법은 무엇입니까?


255

시나리오 : 사용자가 뷰 컨트롤러의 버튼을 탭합니다. 뷰 컨트롤러는 내비게이션 스택에서 최상위입니다 (분명히). 탭은 다른 클래스에서 호출 된 유틸리티 클래스 메소드를 호출합니다. 나쁜 일이 발생하고 컨트롤이 뷰 컨트롤러로 돌아 가기 전에 바로 경고를 표시하고 싶습니다.

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

이것은 가능 UIAlertView했지만 (아마도 적절하지는 않습니다).

이 경우, 당신은 어떻게을 제시 할 UIAlertController바로 거기, myUtilityMethod?

답변:


34

몇 달 전에 비슷한 질문을 게시했으며 마침내 문제를 해결했다고 생각합니다. 코드를 보려면 내 게시물 하단의 링크를 따르십시오.

해결책은 추가 UIWindow를 사용하는 것입니다.

UIAlertController를 표시하려는 경우 :

  1. 창을 키 및 보이는 창으로 설정 ( window.makeKeyAndVisible())
  2. 일반 UIViewController 인스턴스를 새 창의 rootViewController로 사용하십시오. ( window.rootViewController = UIViewController())
  3. 창의 rootViewController에 UIAlertController를 제시하십시오.

몇 가지 참고할 사항 :

  • UIWindow를 강력하게 참조해야합니다. 강력하게 참조되지 않으면 릴리스되지 않으므로 표시되지 않습니다. 속성을 사용하는 것이 좋지만 관련 object 로도 성공했습니다 .
  • 시스템 UIAlertControllers를 포함하여 다른 모든 것 위에 창을 표시하기 위해 windowLevel을 설정했습니다. ( window.windowLevel = UIWindowLevelAlert + 1)

마지막으로, 당신이 그것을보고 싶다면 구현이 완료되었습니다.

https://github.com/dbettermann/DBAlertController


Objective-C에 대해서는 이것을 가지고 있지 않습니까?
SAHM

2
예, 심지어 Swift 2.0 / iOS 9에서도 작동합니다. 다른 누군가가 요청 했으므로 Objective-C 버전으로 작업하고 있습니다. 완료되면 다시 게시하겠습니다.
Dylan Bettermann

322

WWDC에서 랩 중 하나에 들러서 Apple 엔지니어에게 다음과 같은 질문을했습니다. " UIAlertController? 를 표시하는 가장 좋은 방법은 무엇입니까 ?" 그리고 그는 그들이이 질문을 많이 받고 있다고 말했다. 그리고 우리는 그들이 그것에 관한 세션을 가져야한다고 농담했다. 그는 내부적으로 애플이 UIWindow투명성을 가진 제품을 만들고 그것을 UIViewController제시하고 UIAlertController있다고 말했다. 기본적으로 Dylan Betterman의 답변에는 무엇이 있습니까?

그러나 하위 클래스를 사용하고 싶지 않았으므로 UIAlertController앱 전체에서 코드를 변경해야하기 때문입니다. 그래서 관련된 객체의 도움으로 Objective-C UIAlertControllershow메소드 를 제공하는 카테고리를 만들었습니다 .

관련 코드는 다음과 같습니다.

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

UIWindow이 때 생성되는이 파괴됩니다 UIAlertControllerdealloced있다가 유지되는 유일한 객체이기 때문에 UIWindow. 그러나 UIAlertController액션 블록 중 하나에서 경고에 액세스하여 속성에 속성 을 할당 하거나 유지 횟수를 늘리면 UIWindow화면에 그대로 유지되고 UI가 잠 깁니다. 액세스가 필요한 경우를 피하려면 위의 샘플 사용 코드를 참조하십시오 UITextField.

테스트 프로젝트로 깃 허브 저장소를 만들었습니다 : FFGlobalAlertController


1
좋은 물건! 일부 배경-Swift를 사용했기 때문에 관련 객체 대신 하위 클래스를 사용했습니다. 관련 객체는 Objective-C 런타임의 기능이며 이에 의존하고 싶지 않습니다. Swift는 아마도 자체 런타임을 얻는 데 몇 년이 걸리지 만 여전히 있습니다. :)
Dylan Bettermann 2016 년

1
나는 당신의 대답의 우아함을 정말로 좋아하지만 새 창을 어떻게 은퇴하고 원래 창을 다시 키로 만드는지 궁금합니다.
Dustin Pfannenstiel

1
키 창은 맨 위에 보이는 창이므로 "키"창을 제거하거나 숨기면 다음에 보이는 창은 "키"가됩니다.
agilityvision

19
viewDidDisappear:카테고리를 구현 하는 것은 나쁜 생각처럼 보입니다. 본질적으로, 당신은 프레임 워크의 구현과 경쟁하고 viewDidDisappear:있습니다. 현재로서는 괜찮을지 모르지만 나중에 Apple이 해당 메소드를 구현하기로 결정한 경우,이를 호출 할 수있는 방법이 없습니다 (즉 super, 카테고리 구현에서 메소드의 기본 구현을 가리키는 점 은 유사하지 않습니다 ) .
adib

5
좋은,하지만 어떻게 치료하기 위해 노력 prefersStatusBarHidden하고 preferredStatusBarStyle별도의 서브 클래스없이?
Kevin Flachsmann

109

빠른

let alertController = UIAlertController(title: "title", message: "message", preferredStyle: .alert)
//...
var rootViewController = UIApplication.shared.keyWindow?.rootViewController
if let navigationController = rootViewController as? UINavigationController {
    rootViewController = navigationController.viewControllers.first
}
if let tabBarController = rootViewController as? UITabBarController {
    rootViewController = tabBarController.selectedViewController
}
//...
rootViewController?.present(alertController, animated: true, completion: nil)

목표 -C

UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Title" message:@"message" preferredStyle:UIAlertControllerStyleAlert];
//...
id rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
if([rootViewController isKindOfClass:[UINavigationController class]])
{
    rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
}
if([rootViewController isKindOfClass:[UITabBarController class]])
{
    rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
}
//...
[rootViewController presentViewController:alertController animated:YES completion:nil];

2
+1 이것은 매우 간단한 해결책입니다. (내가 직면 한 문제 : Master / Detail 템플릿의 DetailViewController에 경고 표시-iPhone에서는 표시되지 않음)
David

8
다른 부분을 추가하고 싶을 수도 있습니다. if (rootViewController.presentedViewController! = nil) {rootViewController = rootViewController.presentedViewController; }
DivideByZer0

1
스위프트 3 : '경고'가 '경고'로 이름이 변경되었습니다 : let alertController = UIAlertController (제목 : "제목", 메시지 : "메시지", preferredStyle : .alert)
Kaptain

대신 대리자를 사용하십시오!
앤드류 키르 나

104

Swift 2.2로 다음을 수행 할 수 있습니다.

let alertController: UIAlertController = ...
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(alertController, animated: true, completion: nil)

그리고 스위프트 3.0 :

let alertController: UIAlertController = ...
UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)

12
확인하기 전에 수락했습니다. 이 코드는 루트보기 컨트롤러를 반환합니다.이 경우 내 탐색 컨트롤러입니다. 오류는 발생하지 않지만 경고는 표시되지 않습니다.
Murray Sagal

22
그리고 나는 콘솔에서 알아 차렸다 : Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!.
Murray Sagal

1
@MurraySagal는 내비게이션 컨트롤러가있어 visibleViewController언제 어떤 속성에서 알림을 표시 할 컨트롤러인지 확인할 수 있습니다. 문서
Lubo

2
다른 사람의 작업에 대한 크레딧을 받고 싶지 않기 때문에 그렇게했습니다. 그것은 스위프트 3.0을 위해 수정 한 @ZevEisenberg의 솔루션이었다. 다른 답변을 추가했다면 그가받을만한 투표권을 얻었을 것입니다.
jeet.chanchawat

1
어제, 나는 모든 드라마를 놓쳤다. 그러나 나는 Swift 3에 대한 게시물을 방금 업데이트했다. 나는 새로운 언어 버전에 대한 오래된 답변을 업데이트하는 것에 대한 SO의 정책이 무엇인지 모르겠지만, 개인적으로 그것을 신경 쓰지 않는다. 대답이 정확하다면!
Zev Eisenberg

34

및 / 또는 UIAlertController extension모든 경우에 매우 일반적 입니다 . 현재 화면에 모달 VC가있는 경우에도 작동합니다. UINavigationControllerUITabBarController

용법:

//option 1:
myAlertController.show()
//option 2:
myAlertController.present(animated: true) {
    //completion code...
}

이것은 확장입니다 :

//Uses Swift1.2 syntax with the new if-let
// so it won't compile on a lower version.
extension UIAlertController {

    func show() {
        present(animated: true, completion: nil)
    }

    func present(#animated: Bool, completion: (() -> Void)?) {
        if let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController {
            presentFromController(rootVC, animated: animated, completion: completion)
        }
    }

    private func presentFromController(controller: UIViewController, animated: Bool, completion: (() -> Void)?) {
        if  let navVC = controller as? UINavigationController,
            let visibleVC = navVC.visibleViewController {
                presentFromController(visibleVC, animated: animated, completion: completion)
        } else {
          if  let tabVC = controller as? UITabBarController,
              let selectedVC = tabVC.selectedViewController {
                presentFromController(selectedVC, animated: animated, completion: completion)
          } else {
              controller.presentViewController(self, animated: animated, completion: completion)
          }
        }
    }
}

1
나는이 솔루션을 사용하고 있었고, 정말 완벽하고 우아하고 깨끗하다는 것을 알았습니다 ... 그러나 최근에는 루트 뷰 컨트롤러를 뷰 계층 구조가 아닌 뷰로 변경해야했기 때문에이 코드는 쓸모 없게되었습니다. 이것을 계속 사용하기 위해 딕스를 생각하는 사람이 있습니까?

1
나는 다른 sometinhg이 솔루션의 조합을 사용 : 나는 싱글이 UI(! 약)을 보유하고 클래스 currentVC타입의 UIViewController.I이 가지고 BaseViewController있는 상속에서 UIViewController와 설정 UI.currentVCselfviewDidAppear에 다음 nil에를 viewWillDisappear. 앱의 모든 내보기 컨트롤러가 상속 BaseViewController합니다. 그런 식으로 뭔가가 있다면 UI.currentVC(그렇지 않습니다 nil...)-프리젠 테이션 애니메이션의 중간에 있지 않으며,을 제시하도록 요청할 수 있습니다 UIAlertController.
Aviel Gross

1
아래의 당으로, 루트 뷰 컨트롤러는 내가 추가 한 경우에 문이 실패 지난 경우, 그래서 거랑 뭔가를 제시 할 수 else { if let presentedViewController = controller.presentedViewController { presentedViewController.presentViewController(self, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } }
니클라스

27

agilityvision의 답변을 개선 하려면 투명한 루트보기 컨트롤러가있는 창을 만들고 거기에서 경고보기를 제공해야합니다.

그러나 경보 제어기에 조치 가있는 한 창에 대한 참조를 유지할 필요는 없습니다 . 작업 처리기 블록의 마지막 단계로 정리 작업의 일부로 창을 숨기면됩니다. 핸들러 블록에서 창에 대한 참조를 가짐으로써 경보 제어기가 해제되면 끊어지는 임시 순환 참조를 작성합니다.

UIWindow* window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
window.rootViewController = [UIViewController new];
window.windowLevel = UIWindowLevelAlert + 1;

UIAlertController* alertCtrl = [UIAlertController alertControllerWithTitle:... message:... preferredStyle:UIAlertControllerStyleAlert];

[alertCtrl addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK",@"Generic confirm") style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
    ... // do your stuff

    // very important to hide the window afterwards.
    // this also keeps a reference to the window until the action is invoked.
    window.hidden = YES;
}]];

[window makeKeyAndVisible];
[window.rootViewController presentViewController:alertCtrl animated:YES completion:nil];

완벽, 내가 창을
닫는

25

다음 솔루션은 모든 버전에서 유망한 것으로 보였지만 작동 하지 않았습니다 . 이 솔루션은 WARNING을 생성하고 있습니다.

경고 : 보기가 창 계층 구조에없는 경우 표시하십시오!

https://stackoverflow.com/a/34487871/2369867 => 이것은 유망한 것으로 보입니다. 그러나에 없었 습니다 Swift 3. 그래서 나는 스위프트 3에서 이것을 대답하고 있으며 이것은 템플릿 예제 가 아닙니다 .

함수 안에 붙여 넣은 후에는 완전히 기능적인 코드입니다.

빠른 Swift 3 자체 포함 코드

let alertController = UIAlertController(title: "<your title>", message: "<your message>", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Close", style: UIAlertActionStyle.cancel, handler: nil))

let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.present(alertController, animated: true, completion: nil)

이것은 Swift 3에서 테스트되고 작동하는 코드입니다.


1
이 코드는 루트 뷰 컨트롤러가로드되기 전에 마이그레이션 문제와 관련하여 AppDelegate에서 UIAlertController가 시작된 상황에서 완벽하게 작동했습니다. 경고없이 잘 작동했습니다.
던컨 배비지

3
단지 알림 : 당신에 대한 강한 참조를 저장해야합니다. UIWindow그렇지 않으면 창이 해제되고 범위를 벗어난 직후에 사라집니다.
사이렌

24

다음 은 Swift 4에서 테스트 및 작동하는 확장 프로그램에 대한 신화 적 인 답변입니다 .

extension UIAlertController {

    func presentInOwnWindow(animated: Bool, completion: (() -> Void)?) {
        let alertWindow = UIWindow(frame: UIScreen.main.bounds)
        alertWindow.rootViewController = UIViewController()
        alertWindow.windowLevel = UIWindowLevelAlert + 1;
        alertWindow.makeKeyAndVisible()
        alertWindow.rootViewController?.present(self, animated: animated, completion: completion)
    }

}

사용법 예 :

let alertController = UIAlertController(title: "<Alert Title>", message: "<Alert Message>", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Close", style: .cancel, handler: nil))
alertController.presentInOwnWindow(animated: true, completion: {
    print("completed")
})

sharedApplication에 액세스 할 수없는 경우에도 사용할 수 있습니다!
Alfi

20

이것은 일반적인 뷰 컨트롤러와 화면에 탐색 컨트롤러가있는 경우에도 스위프트에서 작동합니다.

let alert = UIAlertController(...)

let alertWindow = UIWindow(frame: UIScreen.main.bounds)
alertWindow.rootViewController = UIViewController()
alertWindow.windowLevel = UIWindowLevelAlert + 1;
alertWindow.makeKeyAndVisible()
alertWindow.rootViewController?.presentViewController(alert, animated: true, completion: nil)

1
경고를 해제하면 UIWindow응답하지 않습니다. windowLevel아마 뭔가와 관련이 있습니다. 반응 형으로 만들려면 어떻게해야합니까?
슬라이더

1
새 창이 닫히지 않은 것 같습니다.
Igor Kulagin 2016 년

창이 위에서 제거되지 않는 것처럼 보이므로 완료되면 창을 제거해야합니다.
soan saini

사용자의 설정 alertWindow에이 nil작업을 종료 할 때.
C6Silver

13

Zev의 답변에 추가하고 Objective-C로 다시 전환하면 루트보기 컨트롤러가 segue 또는 다른 것을 통해 다른 VC를 제시하는 상황에 처할 수 있습니다. 루트 VC에서 presentViewController를 호출하면 다음을 처리합니다.

[[UIApplication sharedApplication].keyWindow.rootViewController.presentedViewController presentViewController:alertController animated:YES completion:^{}];

이것은 루트 VC가 다른 VC로 연결되는 문제를 해결했으며 경고 컨트롤러를 표시하는 대신 위에보고 된 것과 같은 경고가 발생했습니다.

Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!

테스트하지는 않았지만 루트 VC가 탐색 컨트롤러 인 경우에도 필요할 수 있습니다.


Hum Swift 에서이 문제가 발생하여 objc 코드를 신속하게 번역하는 방법을 찾지 못했습니다. 도움이 많이 필요합니다!

2
Objective-C를 Swift로 번역하는 @Mayerz는 그렇게 큰 일이 아니어야합니다.) 그러나 여기 있습니다 :UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
borchero

고마워 올리비에, 네 말이 맞아 파이처럼 쉽고, 나는 이것을 이런 식으로 번역했지만 문제는 다른 곳에 놓여있었습니다. 어쨌든 고마워!

Attempting to load the view of a view controller while it is deallocating is not allowed and may result in undefined behavior (<UIAlertController: 0x15cd4afe0>)
Mojo66

2
나는이 같은 접근 방식을 갔다를 사용하여 rootViewController.presentedViewController자사의 전무하지, 그렇지 않으면 사용 rootViewController. 완전히 일반적인 해결책을 presentedViewControllertopmost
원하면

9

@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)
    }
}

나는 받아 들여진 대답으로 검은 배경을 얻었습니다. window.backgroundColor = UIColor.clear고쳤다. viewController.view.backgroundColor = UIColor.clear필요하지 않은 것 같습니다.
벤 패치

Apple은 UIAlertController서브 클래 싱 에 대해 경고합니다 . The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified. developer.apple.com/documentation/uikit/uialertcontroller
Grubas

6

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

4

이 두 스레드가 속임수로 표시되지 않기 때문에 내 대답을 교차 게시하십시오 ...

이제 UIViewController응답자 체인의 일부이므로 다음과 같이 할 수 있습니다.

if let vc = self.nextResponder()?.targetForAction(#selector(UIViewController.presentViewController(_:animated:completion:)), withSender: self) as? UIViewController {

    let alert = UIAlertController(title: "A snappy title", message: "Something bad happened", preferredStyle: .Alert)
    alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))

    vc.presentViewController(alert, animated: true, completion: nil)
}

4

Zev Eisenberg의 대답은 간단하고 간단하지만 항상 작동하지는 않으며 다음 경고 메시지와 함께 실패 할 수 있습니다.

Warning: Attempt to present <UIAlertController: 0x7fe6fd951e10>  
 on <ThisViewController: 0x7fe6fb409480> which is already presenting 
 <AnotherViewController: 0x7fe6fd109c00>

Windows rootViewController가 제시된보기의 맨 위에 있지 않기 때문입니다. 이 문제를 해결하려면 Swift 3으로 작성된 UIAlertController 확장 코드에 표시된대로 프레젠테이션 체인을 따라야합니다.

   /// show the alert in a view controller if specified; otherwise show from window's root pree
func show(inViewController: UIViewController?) {
    if let vc = inViewController {
        vc.present(self, animated: true, completion: nil)
    } else {
        // find the root, then walk up the chain
        var viewController = UIApplication.shared.keyWindow?.rootViewController
        var presentedVC = viewController?.presentedViewController
        while presentedVC != nil {
            viewController = presentedVC
            presentedVC = viewController?.presentedViewController
        }
        // now we present
        viewController?.present(self, animated: true, completion: nil)
    }
}

func show() {
    show(inViewController: nil)
}

2017 년 9 월 15 일 업데이트 :

위의 논리가 새로 사용 가능한 iOS 11 GM 시드에서 여전히 잘 작동하는지 테스트하고 확인했습니다. 그러나 민첩성 비전에 의해 가장 많이 투표 된 방법은 그렇지 않습니다 UIWindow. iOS 11에서는 키보드 창의 레벨보다 높은 모든 윈도우 레벨이 그 아래 레벨로 낮아지기 때문입니다.

에서 제시 한 유물 keyWindow하지만 키보드의 애니메이션이 경고가 발표 될 때 아래로 슬라이딩 및 경보를 해제 할 때 다시 슬라이딩입니다. 프리젠 테이션 중에 키보드를 그대로 두려면 아래 코드와 같이 상단 창 자체에서 프리젠 테이션을 시도 할 수 있습니다.

func show(inViewController: UIViewController?) {
    if let vc = inViewController {
        vc.present(self, animated: true, completion: nil)
    } else {
        // get a "solid" window with the highest level
        let alertWindow = UIApplication.shared.windows.filter { $0.tintColor != nil || $0.className() == "UIRemoteKeyboardWindow" }.sorted(by: { (w1, w2) -> Bool in
            return w1.windowLevel < w2.windowLevel
        }).last
        // save the top window's tint color
        let savedTintColor = alertWindow?.tintColor
        alertWindow?.tintColor = UIApplication.shared.keyWindow?.tintColor

        // walk up the presentation tree
        var viewController = alertWindow?.rootViewController
        while viewController?.presentedViewController != nil {
            viewController = viewController?.presentedViewController
        }

        viewController?.present(self, animated: true, completion: nil)
        // restore the top window's tint color
        if let tintColor = savedTintColor {
            alertWindow?.tintColor = tintColor
        }
    }
}

위의 코드에서 유일하지 않은 부분은 클래스 이름 UIRemoteKeyboardWindow을 검사하여 클래스 이름도 포함 시킬 수 있는지 확인하는 것입니다. 그럼에도 불구하고 위의 코드는 올바른 색조 색상과 키보드 슬라이딩 아티팩트가없는 iOS 9, 10 및 11 GM 시드에서 훌륭하게 작동합니다.


방금 이전의 많은 답변을 겪어 보았고 Kevin Sliech의 답변을 보았습니다. 케빈 슬리 치 (Kevin Sliech)의 답변은 비슷한 접근 방식으로 동일한 문제를 해결하려고 시도하지만 프레젠테이션 체인을 걷는 데 부족했기 때문에 해결하려는 것과 동일한 오류에 취약합니다. .
CodeBrew

4

스위프트 4+

해결책 전혀 문제없이 몇 년 동안 사용합니다. 우선은 UIWindowvisibleViewController를 찾기 위해 확장 합니다. 참고 : 사용자 지정 컬렉션 * 클래스 (예 : 사이드 메뉴)를 사용하는 경우 다음 확장명에이 경우 처리기를 추가해야합니다. 대부분의 뷰 컨트롤러를 얻은 후에는 다음 UIAlertController과 같이 쉽게 표현할 수 UIAlertView있습니다.

extension UIAlertController {

  func show(animated: Bool = true, completion: (() -> Void)? = nil) {
    if let visibleViewController = UIApplication.shared.keyWindow?.visibleViewController {
      visibleViewController.present(self, animated: animated, completion: completion)
    }
  }

}

extension UIWindow {

  var visibleViewController: UIViewController? {
    guard let rootViewController = rootViewController else {
      return nil
    }
    return visibleViewController(for: rootViewController)
  }

  private func visibleViewController(for controller: UIViewController) -> UIViewController {
    var nextOnStackViewController: UIViewController? = nil
    if let presented = controller.presentedViewController {
      nextOnStackViewController = presented
    } else if let navigationController = controller as? UINavigationController,
      let visible = navigationController.visibleViewController {
      nextOnStackViewController = visible
    } else if let tabBarController = controller as? UITabBarController,
      let visible = (tabBarController.selectedViewController ??
        tabBarController.presentedViewController) {
      nextOnStackViewController = visible
    }

    if let nextOnStackViewController = nextOnStackViewController {
      return visibleViewController(for: nextOnStackViewController)
    } else {
      return controller
    }
  }

}

4

iOS 13의 경우 mythicalcoderbobbyrehm 의 답변을 기반으로합니다 .

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)
    }
}

경고를 수동으로 해제해야하는 경우에는 작동하지 않습니다. WindowAlertPresentationController가 할당 해제되지 않아 UI가 정지됩니다. 여전히 창으로 인해 대화식으로 표시되는 것이 없습니다.
JBlake

경고를 수동으로 dismiss해제하려면 WindowAlertPresentationController를 직접 호출해야합니다.alert.presentingViewController?.dismiss(animated: true, completion: nil)
JBlake

let alertController = UIAlertController (제목 : "제목", 메시지 : "메시지", preferredStyle : .alert); alertController.presentInOwnWindow (animated : false, completion : nil)는 저에게 효과적입니다! 감사!
브라이언

iOS 12.4.5의 iPhone 6에서는 작동하지만 iOS 13.3.1의 iPhone 11 Pro에서는 작동하지 않습니다. 오류는 없지만 경고가 표시되지 않습니다. 어떤 제안이라도 감사하겠습니다.
jl303

iOS 13에서 잘 작동합니다. Catalyst에서 작동하지 않습니다. 일단 경고가 해제되면 앱이 상호 작용할 수 없습니다. @Peter Lapisu의 솔루션
JBlake

3

Objective-C에서 경고를 표시하는 간단한 방법 :

[[[[UIApplication sharedApplication] keyWindow] rootViewController] presentViewController:alertController animated:YES completion:nil];

alertController당신의 UIAlertController목적 은 어디에 있습니까 ?

참고 : 또한 도우미 클래스가 확장되는지 확인해야합니다. UIViewController


3

누군가 관심이 있다면 Swift 3 버전의 @agilityvision 답변을 만들었습니다. 코드:

import Foundation
import UIKit

extension UIAlertController {

    var window: UIWindow? {
        get {
            return objc_getAssociatedObject(self, "window") as? UIWindow
        }
        set {
            objc_setAssociatedObject(self, "window", newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        self.window?.isHidden = true
        self.window = nil
    }

    func show(animated: Bool = true) {
        let window = UIWindow(frame: UIScreen.main.bounds)
        window.rootViewController = UIViewController(nibName: nil, bundle: nil)

        let delegate = UIApplication.shared.delegate
        if delegate?.window != nil {
            window.tintColor = delegate!.window!!.tintColor
        }

        window.windowLevel = UIApplication.shared.windows.last!.windowLevel + 1

        window.makeKeyAndVisible()
        window.rootViewController!.present(self, animated: animated, completion: nil)

        self.window = window
    }
}

@Chathuranga : 편집 내용을 되돌 렸습니다. "오류 처리"는 완전히 불필요합니다.
Martin R

2
extension UIApplication {
    /// The top most view controller
    static var topMostViewController: UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.visibleViewController
    }
}

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else {
            return self
        }
    }
}

이를 통해 다음과 같이 쉽게 경고를 표시 할 수 있습니다

UIApplication.topMostViewController?.present(viewController, animated: true, completion: nil)

주목할 것은 UIAlertController가 현재 표시되어 있으면를 UIApplication.topMostViewController반환한다는 것 UIAlertController입니다. 이상으로 제시하는 UIAlertController것은 이상한 행동이므로 피해야합니다. 따라서 !(UIApplication.topMostViewController is UIAlertController)제시하기 전에 수동으로 확인 하거나 else ifnil을 반환 하는 사례를 추가 해야합니다.self is UIAlertController

extension UIViewController {
    /// The visible view controller from a given view controller
    var visibleViewController: UIViewController? {
        if let navigationController = self as? UINavigationController {
            return navigationController.topViewController?.visibleViewController
        } else if let tabBarController = self as? UITabBarController {
            return tabBarController.selectedViewController?.visibleViewController
        } else if let presentedViewController = presentedViewController {
            return presentedViewController.visibleViewController
        } else if self is UIAlertController {
            return nil
        } else {
            return self
        }
    }
}

1

현재보기 또는 컨트롤러를 매개 변수로 보낼 수 있습니다.

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

예, 가능하고 작동합니다. 그러나 나에게는 약간의 코드 냄새가납니다. 전달 된 매개 변수는 일반적으로 호출 된 메소드가 기본 기능을 수행하는 데 필요합니다. 또한 기존의 모든 통화를 수정해야합니다.
Murray Sagal

1

Kevin Sliech는 훌륭한 솔루션을 제공했습니다.

이제 기본 UIViewController 하위 클래스에서 아래 코드를 사용합니다.

내가 한 작은 변경은 최고의 프리젠 테이션 컨트롤러가 일반 UIViewController가 아닌지 확인하는 것이 었습니다. 그렇지 않은 경우 일반 VC를 제공하는 VC 여야합니다. 따라서 대신 제시되는 VC를 반환합니다.

- (UIViewController *)bestPresentationController
{
    UIViewController *bestPresentationController = [UIApplication sharedApplication].keyWindow.rootViewController;

    if (![bestPresentationController isMemberOfClass:[UIViewController class]])
    {
        bestPresentationController = bestPresentationController.presentedViewController;
    }    

    return bestPresentationController;
}

내 테스트에서 지금까지 모두 해결 된 것 같습니다.

케빈 감사합니다!


1

주어진 훌륭한 답변 외에도 ( agilityvision , adib , malhal ). 오래된 UIAlertViews (경고 창 겹침 방지)에서와 같이 대기열 동작에 도달하려면이 블록을 사용하여 창 수준의 가용성을 관찰하십시오.

@interface UIWindow (WLWindowLevel)

+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block;

@end

@implementation UIWindow (WLWindowLevel)

+ (void)notifyWindowLevelIsAvailable:(UIWindowLevel)level withBlock:(void (^)())block {
    UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
    if (keyWindow.windowLevel == level) {
        // window level is occupied, listen for windows to hide
        id observer;
        observer = [[NSNotificationCenter defaultCenter] addObserverForName:UIWindowDidBecomeHiddenNotification object:keyWindow queue:nil usingBlock:^(NSNotification *note) {
            [[NSNotificationCenter defaultCenter] removeObserver:observer];
            [self notifyWindowLevelIsAvailable:level withBlock:block]; // recursive retry
        }];

    } else {
        block(); // window level is available
    }
}

@end

완전한 예 :

[UIWindow notifyWindowLevelIsAvailable:UIWindowLevelAlert withBlock:^{
    UIWindow *alertWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    alertWindow.windowLevel = UIWindowLevelAlert;
    alertWindow.rootViewController = [UIViewController new];
    [alertWindow makeKeyAndVisible];

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Alert" message:nil preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
        alertWindow.hidden = YES;
    }]];

    [alertWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}];

이렇게하면 경고 창이 겹치지 않도록 할 수 있습니다. 동일한 방법을 사용하여 여러 창 레이어에 대해 큐보기 컨트롤러를 분리하고 넣을 수 있습니다.


1

언급 된 모든 것을 시도했지만 성공하지 못했습니다. 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)
        }
    }
}

1

이 답변 중 일부는 나를 위해 부분적으로 만 효과가 있었으며 AppDelegate의 다음 클래스 방법으로 결합하여 나에게 해결책이었습니다. 모달을 표시 할 때 iPad, UITabBarController보기, UINavigationController에서 작동합니다. iOS 10 및 13에서 테스트되었습니다.

+ (UIViewController *)rootViewController {
    UIViewController *rootViewController = [UIApplication sharedApplication].delegate.window.rootViewController;
    if([rootViewController isKindOfClass:[UINavigationController class]])
        rootViewController = ((UINavigationController *)rootViewController).viewControllers.firstObject;
    if([rootViewController isKindOfClass:[UITabBarController class]])
        rootViewController = ((UITabBarController *)rootViewController).selectedViewController;
    if (rootViewController.presentedViewController != nil)
        rootViewController = rootViewController.presentedViewController;
    return rootViewController;
}

용법:

[[AppDelegate rootViewController] presentViewController ...

1

iOS13 장면 지원 (UIWindowScene 사용시)

import UIKit

private var windows: [String:UIWindow] = [:]

extension UIWindowScene {
    static var focused: UIWindowScene? {
        return UIApplication.shared.connectedScenes
            .first { $0.activationState == .foregroundActive && $0 is UIWindowScene } as? UIWindowScene
    }
}

class StyledAlertController: UIAlertController {

    var wid: String?

    func present(animated: Bool, completion: (() -> Void)?) {

        //let window = UIWindow(frame: UIScreen.main.bounds)
        guard let window = UIWindowScene.focused.map(UIWindow.init(windowScene:)) else {
            return
        }
        window.rootViewController = UIViewController()
        window.windowLevel = .alert + 1
        window.makeKeyAndVisible()
        window.rootViewController!.present(self, animated: animated, completion: completion)

        wid = UUID().uuidString
        windows[wid!] = window
    }

    open override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if let wid = wid {
            windows[wid] = nil
        }

    }

}

0

And와 UIViewController같은 mehtod 로 카테고리를 구현하려고 시도 할 수 있으며 - (void)presentErrorMessage;그 메소드 내에서 UIAlertController를 구현 한 다음에 제시하십시오 self. 클라이언트 코드보다 다음과 같은 것이 있습니다.

[myViewController presentErrorMessage];

이렇게하면 뷰가 창 계층 구조에 있지 않다는 불필요한 매개 변수와 경고를 피할 수 있습니다.


myViewController코드에 나쁜 일 이없는 것을 제외하고는 . 그것은 그것을 호출하는 뷰 컨트롤러에 대해 아무것도 모르는 유틸리티 메소드에 있습니다.
Murray Sagal

2
IMHO는 모든 뷰 (따라서 경고)를 사용자에게 제시하는 것은 ViewController의 책임입니다. 따라서 코드의 일부가 viewController에 대해 아무것도 모른다면 사용자에게 오류를 나타내지 말고 코드의 "viewController aware"부분으로 전달해야합니다.
Vlad Soroka

2
나는 동의한다. 그러나 이제는 더 이상 사용되지 않는 편의로 UIAlertView인해 몇 가지 지점에서 그 규칙을 위반했습니다.
Murray Sagal

0

사용할 수있는 두 가지 접근 방식이 있습니다.

- UIAlertView또는 대신 'UIActionSheet'을 사용하십시오 (권장하지 않음, iOS 8에서 더 이상 사용되지 않지만 지금 작동 함)

-마지막으로 표시되는 마지막 뷰 컨트롤러를 기억하십시오. 여기 예가 있습니다.

@interface UIViewController (TopController)
+ (UIViewController *)topViewController;
@end

// implementation

#import "UIViewController+TopController.h"
#import <objc/runtime.h>

static __weak UIViewController *_topViewController = nil;

@implementation UIViewController (TopController)

+ (UIViewController *)topViewController {
    UIViewController *vc = _topViewController;
    while (vc.parentViewController) {
        vc = vc.parentViewController;
    }
    return vc;
}

+ (void)load {
    [super load];
    [self swizzleSelector:@selector(viewDidAppear:) withSelector:@selector(myViewDidAppear:)];
    [self swizzleSelector:@selector(viewWillDisappear:) withSelector:@selector(myViewWillDisappear:)];
}

- (void)myViewDidAppear:(BOOL)animated {
    if (_topViewController == nil) {
        _topViewController = self;
    }

    [self myViewDidAppear:animated];
}

- (void)myViewWillDisappear:(BOOL)animated {
    if (_topViewController == self) {
        _topViewController = nil;
    }

    [self myViewWillDisappear:animated];
}

+ (void)swizzleSelector:(SEL)sel1 withSelector:(SEL)sel2
{
    Class class = [self class];

    Method originalMethod = class_getInstanceMethod(class, sel1);
    Method swizzledMethod = class_getInstanceMethod(class, sel2);

    BOOL didAddMethod = class_addMethod(class,
                                        sel1,
                                        method_getImplementation(swizzledMethod),
                                        method_getTypeEncoding(swizzledMethod));

    if (didAddMethod) {
        class_replaceMethod(class,
                            sel2,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@end 

용법:

[[UIViewController topViewController] presentViewController:alertController ...];

0

AppDelegate 클래스에서 약간의 개인적인 변형 으로이 코드를 사용합니다.

-(UIViewController*)presentingRootViewController
{
    UIViewController *vc = self.window.rootViewController;
    if ([vc isKindOfClass:[UINavigationController class]] ||
        [vc isKindOfClass:[UITabBarController class]])
    {
        // filter nav controller
        vc = [AppDelegate findChildThatIsNotNavController:vc];
        // filter tab controller
        if ([vc isKindOfClass:[UITabBarController class]]) {
            UITabBarController *tbc = ((UITabBarController*)vc);
            if ([tbc viewControllers].count > 0) {
                vc = [tbc viewControllers][tbc.selectedIndex];
                // filter nav controller again
                vc = [AppDelegate findChildThatIsNotNavController:vc];
            }
        }
    }
    return vc;
}
/**
 *   Private helper
 */
+(UIViewController*)findChildThatIsNotNavController:(UIViewController*)vc
{
    if ([vc isKindOfClass:[UINavigationController class]]) {
        if (((UINavigationController *)vc).viewControllers.count > 0) {
            vc = [((UINavigationController *)vc).viewControllers objectAtIndex:0];
        }
    }
    return vc;
}

0

작동하는 것 같습니다 :

static UIViewController *viewControllerForView(UIView *view) {
    UIResponder *responder = view;
    do {
        responder = [responder nextResponder];
    }
    while (responder && ![responder isKindOfClass:[UIViewController class]]);
    return (UIViewController *)responder;
}

-(void)showActionSheet {
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
    [alertController addAction:[UIAlertAction actionWithTitle:@"Do it" style:UIAlertActionStyleDefault handler:nil]];
    [viewControllerForView(self) presentViewController:alertController animated:YES completion:nil];
}

0

헬퍼 클래스 AlertWindow를 작성하고

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);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.