Bir görünüm denetleyicisinde olmadığında UIAlertController nasıl sunulur?


255

Senaryo: Kullanıcı bir görünüm denetleyicisindeki düğmeye basar. Görünüm denetleyicisi, gezinme yığınında en üstte (açıkçası). Musluk, başka bir sınıfta çağrılan bir yardımcı sınıf yöntemini çağırır. Orada kötü bir şey olur ve kontrol görünüm denetleyicisine dönmeden önce bir uyarı görüntülemek istiyorum.

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

Bu mümkün UIAlertView(ama belki de pek uygun değil).

Bu durumda, nasıl bir mevcut do UIAlertControllersağ orada, myUtilityMethod?

Yanıtlar:


34

Birkaç ay önce benzer bir soru yayınladım ve sonunda sorunu çözdüğümü düşünüyorum. Kodu görmek istiyorsanız, yazımın altındaki bağlantıyı izleyin.

Çözüm, ek bir UIWindow kullanmaktır.

UIAlertController cihazınızı görüntülemek istediğinizde:

  1. Pencerenizi anahtar ve görünür pencere yapın ( window.makeKeyAndVisible())
  2. Yeni pencerenin rootViewController'ı olarak basit bir UIViewController örneği kullanın. ( window.rootViewController = UIViewController())
  3. UIAlertController'ınızı pencerenizin rootViewController'ında gösterin

Unutulmaması gereken birkaç nokta:

  • Kullanıcı Arabiriminize güçlü bir şekilde başvurulmalıdır. Güçlü bir şekilde referans verilmezse hiçbir zaman görünmez (çünkü yayınlanmıştır). Bir özellik kullanmanızı öneririm, ancak ilişkili bir nesneyle de başarılı oldum .
  • Pencerenin (UIAlertControllers sistemi dahil) her şeyin üstünde olmasını sağlamak için windowLevel ayarını yaptım. ( window.windowLevel = UIWindowLevelAlert + 1)

Son olarak, sadece bakmak isterseniz tamamlanmış bir uygulamam var.

https://github.com/dbettermann/DBAlertController


Objective-C için buna sahip değilsiniz, değil mi?
SAHM

2
Evet, Swift 2.0 / iOS 9'da bile çalışıyor. Şu anda bir Objective-C sürümü üzerinde çalışıyorum çünkü başka biri istedi (belki de sensin). İşim bittiğinde geri göndereceğim.
Dylan Bettermann

322

WWDC'de laboratuarlardan birinde durdum ve bir Apple Mühendisine şu aynı soruyu sordum: "Bir görüntülemek için en iyi uygulama UIAlertControllerneydi?" Ve bu soruyu çok fazla aldıklarını söyledi ve biz de bu konuda bir oturum yapmaları gerektiğini söyledik. Dahili olarak Apple'ın UIWindowşeffaf bir ile oluşturduğunu UIViewControllerve daha sonra sunulduğunu UIAlertControllersöyledi. Temelde Dylan Betterman'ın cevabında ne var.

Ancak, bir alt sınıf kullanmak istemiyordum UIAlertControllerçünkü bu benim uygulama boyunca kodumu değiştirmeyi gerektiriyordu. İlişkili bir nesnenin yardımıyla, Objective-C'de UIAlertControllerbir showyöntem sağlayan bir kategori yaptım .

İlgili kod:

#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

İşte örnek bir kullanım:

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

Oluşturulan UIWindowşey, UIAlertControllerdağıtıldığında yok edilir, çünkü onu tutan tek nesne budur UIWindow. Ancak UIAlertController, bir özelliğe atarsanız veya eylem bloklarından birinde uyarıya erişerek alıkoyma sayısının artmasına neden UIWindowolursa, ekranda kalır ve kullanıcı arayüzünüzü kilitler. Erişilmesi gerektiğinde önlemek için yukarıdaki örnek kullanım koduna bakın UITextField.

Bir test projesi ile bir GitHub repo yaptım: FFGlobalAlertController


1
İyi şeyler! Sadece bir arka plan - Swift kullandığım için ilişkili bir nesne yerine bir alt sınıf kullandım. İlişkili nesneler Objective-C çalışma zamanının bir özelliğidir ve ona bağımlı olmak istemedim. Swift muhtemelen kendi çalışma zamanını almaktan yıllarca uzakta, ama yine de. :)
Dylan Bettermann

1
Cevabınızın zarafetini gerçekten çok seviyorum, ancak yeni pencereyi nasıl emekliye ayırdığınızı ve orijinal pencereyi nasıl tekrar anahtar yaptığınızı merak ediyorum (kuşkusuz pencere ile uğraşmıyorum).
Dustin Pfannenstiel

1
Anahtar pencere en üstteki görünür penceredir, bu yüzden benim anlayışım "anahtar" penceresini kaldırır / gizlerseniz, bir sonraki görünür pencere "anahtar" olur.
agilityvision

19
viewDidDisappear:Bir kategoride uygulamak Kötü Bir Fikir gibi görünüyor. Aslında, çerçevenin uygulanmasıyla rekabet ediyorsunuz viewDidDisappear:. Şimdilik iyi olabilir, ancak Apple gelecekte bu yöntemi uygulamaya karar verirse, onu çağırmanın bir yolu yoktur (yani super, bir kategorinin bir uygulamadan bir yöntemin birincil uygulamasına benzer bir anlamı yoktur ) .
adib

5
Büyük, ama nasıl tedavi için çalışır prefersStatusBarHiddenve preferredStatusBarStyleekstra bir alt sınıf olmadan?
Kevin Flachsmann

109

hızlı

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)

Objective-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 Bu son derece basit bir çözüm. (Karşılaştığım sorun: Master / Detail şablonunun DetailViewController'ında bir uyarı görüntüleme - iPad'de gösterir, asla iPhone'da gösterilmez)
David

8
Güzel, başka bir bölüm eklemek isteyebilirsiniz: if (rootViewController.presentedViewController! = Nil) {rootViewController = rootViewController.presentedViewController; }
DivideByZer0

1
Swift 3: 'Alert', 'alert' olarak yeniden adlandırıldı: let alertController = UIAlertController (title: "title", mesaj: "message", tercih edilenStyle: .alert)
Kaptain

Bunun yerine bir temsilci kullanın!
Andrew Kirna

104

Swift 2.2 ile aşağıdakileri yapabilirsiniz:

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

Ve Swift 3.0:

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

12
Hata! Kontrol etmeden önce kabul ettim. Bu kod, benim durumumda navigasyon denetleyicisi olan kök görünüm denetleyicisini döndürür. Hataya neden olmaz, ancak uyarı görüntülenmez.
Murray Sagal

22
Ve konsolda fark ettik: Warning: Attempt to present <UIAlertController: 0x145bfa30> on <UINavigationController: 0x1458e450> whose view is not in the window hierarchy!.
Murray Sagal

1
@MurraySagal bir navigasyon denetleyicisine sahip visibleViewControllerolup, uyarıyı hangi denetleyicinin sunacağını görmek için istediğiniz zaman özelliği alabilirsiniz . Dokümanlara
Lubo

2
Başardım çünkü başka birinin işinin kredisini almak istemiyorum. Hızlı 3.0 için değiştirdiğim @ZevEisenberg çözümü oldu. Bir cevap daha ekleseydim, hak ettiği oyları alabilirdim.
jeet.chanchawat

1
Oh hey, dün tüm dramayı kaçırdım, ancak Swift 3'ün yayınını yeni güncelledim. SO'nun yeni dil sürümleri için eski cevapları güncelleme konusundaki politikasının ne olduğunu bilmiyorum, ama şahsen umursamıyorum, cevap doğru olduğu sürece!
Zev Eisenberg

34

Ve / veya UIAlertController extensiontüm vakalar için oldukça genel . Ayrıca şu anda ekranda kalıcı bir VC varsa da çalışır. UINavigationControllerUITabBarController

Kullanımı:

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

Bu uzantı:

//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
Bu çözümü kullanıyordum ve gerçekten mükemmel, zarif, temiz buldum ... AMA, son zamanlarda kök görünüm denetleyicimi görünüm hiyerarşisinde olmayan bir görünüme değiştirmek zorunda kaldım, bu yüzden bu kod işe yaramaz hale geldi. Bunu kullanmaya devam etmek için bir dix düşünen var mı?

1
Başka sometinhg ile bu çözümün bir arada kullanın: Bir Singleton var UI(! Zayıf) bir tutan sınıf currentVCÇeşidi UIViewController.Ben sahip BaseViewControllerolan devralır gelen UIViewControllerve set UI.currentVCiçin selfüzerine viewDidAppeardaha sonra nilüzerinde viewWillDisappear. Uygulamadaki tüm görünüm denetleyicilerim devralır BaseViewController. Bu şekilde bir şeyiniz varsa UI.currentVC(değil nil...) - bu kesinlikle bir sunum animasyonunun ortasında değildir ve sunmanızı isteyebilirsiniz UIAlertController.
Aviel Gross

1
Aşağıda olduğu gibi, kök görünüm denetleyicisi segue ile bir şey sunabilir, bu durumda son if ifadeniz başarısız olursa, bu yüzden eklemek zorunda kaldım else { if let presentedViewController = controller.presentedViewController { presentedViewController.presentViewController(self, animated: animated, completion: completion) } else { controller.presentViewController(self, animated: animated, completion: completion) } }
Niklas

27

Çeviklik cevabının cevabını iyileştirerek, şeffaf bir kök görünüm denetleyicisine sahip bir pencere oluşturmanız ve oradan uyarı görünümünü sunmanız gerekir.

Ancak , uyarı denetleyicinizde bir eyleminiz olduğu sürece , pencereye referans tutmanıza gerek yoktur . Eylem işleyici bloğunun son adımı olarak, pencereyi temizleme görevinin bir parçası olarak gizlemeniz yeterlidir. İşleyici bloğundaki pencereye bir referans alarak, bu, uyarı kontrolörü kapatıldıktan sonra kırılacak geçici bir dairesel referans oluşturur.

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

Mükemmel, tam ipucu pencereyi kapatmak için gerekli, teşekkürler dostum
thibaut noah

25

Aşağıdaki çözüm yoktu değil oldukça tüm sürümleri ile gelecek vaat görünüyordu rağmen çalışır. Bu çözüm UYARI üretiyor .

Uyarı: Görünümü pencere hiyerarşisinde olmayanlar üzerinde sunum yapmaya çalışın!

https://stackoverflow.com/a/34487871/2369867 => O zaman umut verici görünüyor. Ama oldu değil de Swift 3. Bu yüzden Swift 3'te cevaplıyorum ve bu şablon örneği değil .

Bu, herhangi bir işlevin içine yapıştırdıktan sonra tamamen işlevsel bir koddur.

Hızlı Swift 3 bağımsız kod

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)

Bu test edilmiş ve Swift 3'te çalışma kodu.


1
Bu kod, herhangi bir kök görünüm denetleyicisi yüklenmeden önce, geçiş sorunuyla ilgili olarak Uygulama Temsilcisi'nde bir UIAlertController'ın tetiklendiği bir bağlamda benim için mükemmel çalıştı. Harika çalıştı, uyarı yok.
Duncan Babbage

3
Sadece bir hatırlatma: kendinize güçlü bir referans depolamanız gerekir, UIWindowaksi takdirde pencere serbest bırakılır ve kapsamdan çıktıktan kısa bir süre sonra kaybolur.
Sirenler

24

İşte mythicalcoder cevabı bir uzantısı, test & Swift 4 çalışan olarak:

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

}

Örnek kullanım:

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

Bu, sharedApplication'a erişilemese bile kullanılabilir!
Alfi

20

Bu, Swift'te normal görünüm denetleyicileri için ve ekranda bir gezinme denetleyicisi olsa bile çalışır:

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
Uyarıyı reddettiğimde UIWindow, yanıt vermiyor. windowLevelMuhtemelen bununla ilgili bir şey . Nasıl yanıt verebilirim?
kaydırıcı

1
Yeni pencere yok sayılmadı.
Igor Kulagin

Görünüşe göre Pencere üstten kaldırılmadı, Bu yüzden bitince pencereyi kaldırmanız gerekiyor.
soan saini

İşiniz bittiğinde alertWindowiçin ayarını yapın nil.
C6Silver

13

Zev'in cevabına ekleyerek (ve Objective-C'ye geri dönerek), kök görünüm denetleyicinizin bir segue veya başka bir şey aracılığıyla başka bir VC sunduğu bir duruma girebilirsiniz. Kök VC'de sunulanViewController çağrıldığında bununla ilgilenilir:

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

Bu, kök VC'nin başka bir VC'ye yönlendirdiği bir sorunu düzeltti ve uyarı denetleyicisini sunmak yerine, yukarıda bildirilenler gibi bir uyarı verildi:

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

Test etmedim, ancak kök VC'niz bir navigasyon denetleyicisi olduğunda da bu gerekli olabilir.


Hum Swift'te bu problemle karşılaşıyorum ve objc kodunuzu hızlı bir şekilde nasıl çevireceğinizi bulamıyorum, yardım çok takdir edilecektir!

2
@Mayerz Objective-C'yi Swift'e çevirmek o kadar da büyük bir şey olmamalı;) ama buradasın:UIApplication.sharedApplication().keyWindow?.rootViewController?.presentedViewController?.presentViewController(controller, animated: true, completion: nil)
borchero

Teşekkürler Olivier, haklısın, pasta gibi kolay ve bu şekilde tercüme ettim, ama sorun başka bir yerde yatıyordu. Yine de teşekkürler!

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
Aynı yaklaşımla gittim, rootViewController.presentedViewControllereğer nil değilse kullanın , aksi takdirde kullanarak rootViewController. Tamamen jenerik bir çözüm presentedViewControlleriçin, topmostVC'ye ulaşmak için s zincirini yürümek gerekebilir
Protongun

9

@ agilityvision'un cevabı Swift4 / iOS11'e çevrildi. Yerelleştirilmiş dizeler kullanmadım, ancak bunu kolayca değiştirebilirsiniz:

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

Kabul edilen cevapla birlikte siyah bir arka plan alıyordum. window.backgroundColor = UIColor.clearbunu düzelttim. viewController.view.backgroundColor = UIColor.cleargerekli görünmüyor.
Ben Patch

Apple'ın UIAlertControlleralt sınıflandırma konusunda uyardığını unutmayın : 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 yanıtında olduğu gibi Uzantı oluşturun. Burada Objective-C uzantınız var.

Burada başlık dosyanız * .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

Ve uygulama: * .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

Bu uzantıyı uygulama dosyanızda şu şekilde kullanıyorsunuz:

#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

Bu iki konu dupes olarak işaretlenmediğinden cevabımı çapraz gönder ...

Şimdi bu UIViewControlleryanıtlayıcı zincirinin bir parçası, böyle bir şey yapabilirsiniz:

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'in cevabı basit ve basittir, ancak her zaman işe yaramaz ve bu uyarı mesajıyla başarısız olabilir:

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

Bunun nedeni, windows rootViewController'ın sunulan görünümlerin üstünde olmamasıdır. Bunu düzeltmek için Swift 3'te yazılmış UIAlertController uzantı kodumda gösterildiği gibi sunum zincirine geçmeliyiz:

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

15.09.2017 ile ilgili güncellemeler:

Yukarıdaki mantığın yeni mevcut iOS 11 GM tohumunda hala harika çalıştığını test etti ve onayladı. Bununla birlikte, çeviklik tarafından en çok oylanan yöntem şunları yapmaz: yeni basılmış UIWindowolarak sunulan uyarı görünümü klavyenin altındadır ve kullanıcının düğmelerine dokunmasını engeller. Bunun nedeni, iOS 11'de klavye penceresinden daha yüksek tüm düzeylerin altındaki bir seviyeye düşürülmesidir.

keyWindowYine de sunum yapmanın bir özelliği, uyarı sunulduğunda klavyenin aşağı doğru kayması ve uyarı reddedildiğinde tekrar yukarı kaymasıdır. Klavyenin sunum sırasında orada kalmasını istiyorsanız, aşağıdaki kodda gösterildiği gibi üst pencerenin kendisinden sunmayı deneyebilirsiniz:

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

Yukarıdaki kodun o kadar da büyük olmayan kısmı, UIRemoteKeyboardWindowonu da ekleyebildiğimizden emin olmak için sınıf adını kontrol etmesidir. Bununla birlikte, yukarıdaki kod iOS 9, 10 ve 11 GM tohumunda, doğru renk tonu ile ve klavye kayan eserler olmadan harika çalışıyor.


Burada daha önce verilen birçok cevabı inceledim ve aynı sorunu benzer bir yaklaşımla çözmeye çalışan ama sunum zincirini yürütebilmeyi durduran ve böylece çözmeye çalıştığı hataya duyarlı hale gelen Kevin Sliech'in cevabını gördüm. .
CodeBrew

4

Swift 4+

Çözüm Yıllarca hiçbir sorun olmadan kullanıyorum. Her şeyden önce UIWindowvisibleViewController bulmak için uzatın . NOT : özel koleksiyon * sınıfları (yan menü gibi) kullanıyorsanız, bu uzantı için aşağıdaki uzantıda işleyici eklemeniz gerekir. En üst görünüm denetleyicisi aldıktan sonra onu sunmak kolay UIAlertControllergibi 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

İOS 13 için, efsanevi kodlayıcı ve bobbyrehm'un cevaplarını temel alarak :

İOS 13'te, uyarıyı sunmak için kendi pencerenizi oluşturuyorsanız, o pencereye güçlü bir referans tutmanız gerekir, aksi takdirde uyarınız kapsamdan çıktığında pencere hemen yer değiştireceğinden uyarınız görüntülenmez.

Ayrıca, uyarı kaldırıldıktan sonra, altındaki ana pencerede kullanıcı etkileşimine izin vermek üzere pencereyi kaldırmak için referansı tekrar nil olarak ayarlamanız gerekir.

UIViewControllerPencere belleği yönetim mantığını kapsüllemek için bir alt sınıf oluşturabilirsiniz :

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

Bunu olduğu gibi kullanabilirsiniz veya bir kolaylık yöntemi UIAlertControlleristiyorsanız, bir uzantıya atabilirsiniz:

extension UIAlertController {

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

        let windowAlertPresentationController = WindowAlertPresentationController(alert: self)
        windowAlertPresentationController.present(animated: animated, completion: completion)
    }
}

Uyarıyı manuel olarak kapatmanız gerekirse bu işe yaramaz - WindowAlertPresentationController hiçbir zaman ayrılmaz, donmuş bir kullanıcı arayüzü ile sonuçlanır - pencerenin hala orada olması nedeniyle hiçbir şey etkileşimli değildir
JBlake

Uyarıyı manuel olarak kapatmak istiyorsanız, dismissdoğrudan WindowAlertPresentationController'ı arayınalert.presentingViewController?.dismiss(animated: true, completion: nil)
JBlake

let alertController = UIAlertController (title: "title", mesaj: "message", preferStyle: .alert); alertController.presentInOwnWindow (hareketli: yanlış, tamamlama: nil) benim için harika çalışıyor! Teşekkürler!
Brian

Bu, iOS 12.4.5 ile iPhone 6'da çalışır, ancak iOS 13.3.1 ile iPhone 11 Pro'da çalışmaz. Hata yok, ancak uyarı hiçbir zaman görüntülenmiyor. Her türlü önerinize açığız.
jl303

İOS 13 için harika çalışıyor. Catalyst'te çalışmıyor - uyarı reddedildikten sonra uygulama etkileşim kuramıyor. @Peter Lapisu'nun çözümü
JBlake

3

Uyarıyı Objective-C'de sunmanın kısa yolu:

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

Nerede alertControllersenin olduğu UIAlertControllernesne.

NOT: Ayrıca yardımcı sınıfınızın genişletildiğinden emin olmanız gerekir UIViewController


3

Eğer ilgilenen varsa @agilityvision cevabının Swift 3 versiyonunu oluşturdum. Kod:

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: Düzenlemenizi geri aldım. Bu “hata yönetimi” tamamen gereksizdir.
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
        }
    }
}

Bu şekilde uyarınızı kolayca bu şekilde sunabilirsiniz

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

Dikkat edilmesi gereken bir şey, şu anda görüntülenen bir UIAlertController varsa, UIApplication.topMostViewControllera döndürecektir UIAlertController. Bir üstünde sunmak UIAlertControllergarip bir davranış vardır ve kaçınılmalıdır. Bu nedenle, elle kontrol etmelisiniz !(UIApplication.topMostViewController is UIAlertController)sunmadan önce veya bir ekleme else ifdönüş nil eğer davayı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

Geçerli görünümü veya denetleyiciyi parametre olarak gönderebilirsiniz:

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

Evet, bu mümkün ve işe yarar. Ama benim için biraz kod kokusu var. Çağrılan yöntemin birincil işlevini gerçekleştirmesi için genellikle iletilen parametreler gerekir. Ayrıca mevcut tüm çağrıların değiştirilmesi gerekir.
Murray Sagal

1

Kevin Sliech harika bir çözüm sundu.

Şimdi ana UIViewController alt sınıfında aşağıdaki kodu kullanın.

Yaptığım küçük bir değişiklik, en iyi sunum denetleyicisinin düz bir UIViewController olup olmadığını kontrol etmekti. Değilse, düz bir VC sunan bir VC olmalı. Böylece bunun yerine sunulan VC'ye geri dönüyoruz.

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

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

    return bestPresentationController;
}

Testlerimde şimdiye kadar her şey yolunda görünüyor.

Teşekkürler Kevin!


1

Verilen harika cevaplara ek olarak ( çeviklik , adib , malhal ). Eski UIAlertView'lerde olduğu gibi kuyruğa alma davranışına ulaşmak için (uyarı pencerelerinin çakışmasını önleyin), pencere düzeyinde kullanılabilirliği gözlemlemek için bu bloğu kullanın:

@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

Komple örnek:

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

Bu, uyarı pencerelerinin çakışmasını önleyecektir. Aynı yöntem, herhangi bir sayıda pencere katmanı için kuyruk görünümü denetleyicilerini ayırmak ve koymak için kullanılabilir.


1

Bahsedilen her şeyi denedim, ama başarılı olamadım. Swift 3.0 için kullandığım yöntem:

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

Bu cevapların bazıları sadece benim için çalıştı, bunları AppDelegate'teki aşağıdaki sınıf yönteminde birleştirmek benim için bir çözümdü. Modals sunarken, UITabBarController görünümlerinde, UINavigationController'da iPad üzerinde çalışır. İOS 10 ve 13'te test edildi.

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

Kullanımı:

[[AppDelegate rootViewController] presentViewController ...

1

iOS13 sahne desteği (UIWindowScene kullanırken)

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
        }

    }

}


UIButton da alt sınıflı olmamalı ve herkes ... özel yöntemleri değiştirmediğiniz ve alertcontroller içindeki görünümleri değiştirmediğiniz sürece, tamam
Peter Lapisu

0

Ve UIViewControllergibi mehtod ile bir kategori uygulamayı deneyebilirsiniz - (void)presentErrorMessage;ve bu yöntem içinde UIAlertController uygulamak ve sonra sunun self. Müşteri kodunuzdakinden daha çok şuna sahip olacaksınız:

[myViewController presentErrorMessage];

Bu şekilde, görünümün pencere hiyerarşisinde olmamasıyla ilgili gereksiz parametrelerden ve uyarılardan kaçınacaksınız.


Dışında myViewControllerkötü bir şey nerede kodu yok . Bu, görünüm denetleyicisi hakkında hiçbir şey bilmeyen bir yardımcı program yöntemidir.
Murray Sagal

2
Kullanıcıya herhangi bir görünüm (dolayısıyla uyarı) sunan IMHO, ViewControllers'ın sorumluluğundadır. Kodun bir kısmı viewController hakkında hiçbir şey bilmiyorsa, kullanıcıya herhangi bir hata göstermemeli, daha ziyade kodun "viewController farkında" kısımlarına iletilmelidir
Vlad Soroka

2
Katılıyorum. Ancak şu anda kullanımdan kaldırılanların rahatlığı UIAlertViewbeni bu kuralı birkaç noktada kırmaya itti.
Murray Sagal

0

Kullanabileceğiniz 2 yaklaşım vardır:

-Ya UIAlertViewda 'UIActionSheet'i kullanın (önerilmez, iOS 8'de kullanımdan kaldırılmasına neden olur, ancak şimdi çalışır)

-Sunulan son görünüm denetleyicisini hatırlayın. İşte örnek.

@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 

Kullanımı:

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

0

Bu kodu AppDelegate sınıfımda bazı küçük kişisel varyasyonlarla kullanıyorum

-(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

Çalışıyor gibi görünüyor:

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 yardımcı sınıfı yaratın ve

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);
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.