Как лучше всего проверить, присутствует ли уже UIAlertController?

109

У меня есть табличное представление, которое при загрузке каждая ячейка может возвращать NSError, который я выбрал для отображения в UIAlertController. Проблема в том, что я получаю эту ошибку в консоли, если возвращается несколько ошибок.

Предупреждение: Попытка представить UIAlertController: 0x14e64cb00 в MessagesMasterVC: 0x14e53d800, который уже представлен (null)

В идеале я хотел бы обработать это в моем методе расширения UIAlertController.

class func simpleAlertWithMessage(message: String!) -> UIAlertController {

    let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
    let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)

    alertController.addAction(cancel)
    return alertController
}

Основываясь на ответе Мэтта, я изменил расширение на расширение UIViewController, оно намного чище и сохраняет много кода PresentViewController.

    func showSimpleAlertWithMessage(message: String!) {

    let alertController = UIAlertController(title: nil, message: message, preferredStyle: UIAlertControllerStyle.Alert)
    let cancel = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)

    alertController.addAction(cancel)

    if self.presentedViewController == nil {
        self.presentViewController(alertController, animated: true, completion: nil)
    }
}
скрытое имя пользователя
источник
Спасибо, что разместили обновленный код.
djbp
Я также переместил остальную часть кода (три строки для настройки UIAlertController) в оператор If, потому что он все еще давал следующую ошибку (попытка загрузить представление контроллера представления во время его освобождения недопустима и может привести к undefined behavior)
Китсон,
Я хотел бы сослаться на решение по приведенной ниже ссылке, пожалуйста, проверьте stackoverflow.com/a/39994115/1872233
iDevAmit

Ответы:

118

Это не UIAlertController, который «уже представляет», это MessagesMasterVC. Контроллер представления может одновременно представлять только один другой контроллер представления. Отсюда и сообщение об ошибке.

Другими словами, если вы сказали контроллеру представления presentViewController:..., вы не можете сделать это снова, пока представленный контроллер представления не будет отклонен.

Вы можете спросить MessagesMasterVC, представляет ли он уже контроллер представления, проверив его presentedViewController. Если нет nil, не сообщайте об этом presentViewController:...- он уже представляет контроллер представления.

матовый
источник
2
Если контроллер A представляет контроллер B, а затем B хочет представить UIAlertController, это сработает? У меня такая же ошибка, и я не могу понять, представляет ли B то, о чем я не знаю, или проблема в том, что B представляет A
Кристофер Франциско
1
@ChristopherFrancisco Задайте это как новый вопрос!
Мэтт
@ChristopherFrancisco Привет, у меня такая же проблема, вы задали для нее новый вопрос? или где ты сможешь это решить? если да, то как?
Abed Naseri
Отличный ответ, это тонкое различие.
ScottyBlades
29
if ([self.navigationController.visibleViewController isKindOfClass:[UIAlertController class]]) {

      // UIAlertController is presenting.Here

}
Бен
источник
22
Всегда полезно добавить текст в свой ответ, чтобы объяснить, что вы делаете. Прочтите, как написать хороший ответ .
Jørgen R
1
Не лучший ответ из-за отсутствия объяснения, но метод мне очень помог - проблема заключалась в том, что у меня было несколько событий, вызывающих мой код, чтобы представить UIAlertControllerстрельбу в короткой последовательности. Проверьте это, если у вас есть аналогичная проблема.
ChidG 01
10

Что ж, у предложенных выше решений есть существенная проблема, с моей точки зрения:

Если вы спросите свой ViewController, равен ли атрибут «presentViewController» нулю, а ответ - ложь, вы не сможете прийти к выводу, что ваш UIAlertController уже представлен. Это может быть любой представленный ViewController, например popOver. Итак, мое предложение обязательно проверить, отображается ли предупреждение уже на экране, следующее (приведите представленныйViewController как UIAlertController):

if self.presentedViewController == nil {
   // do your presentation of the UIAlertController
   // ...
} else {
   // either the Alert is already presented, or any other view controller
   // is active (e.g. a PopOver)
   // ...

   let thePresentedVC : UIViewController? = self.presentedViewController as UIViewController?

   if thePresentedVC != nil {
      if let thePresentedVCAsAlertController : UIAlertController = thePresentedVC as? UIAlertController {
         // nothing to do , AlertController already active
         // ...
         print("Alert not necessary, already on the screen !")

      } else {
         // there is another ViewController presented
         // but it is not an UIAlertController, so do 
         // your UIAlertController-Presentation with 
         // this (presented) ViewController
         // ...
         thePresentedVC!.presentViewController(...)

         print("Alert comes up via another presented VC, e.g. a PopOver")
      }
  }

}

ЛюкSideWalker
источник
5

Вот решение, которое я использую в Swift 3. Это функция, которая показывает предупреждение пользователю, и если вы вызовете ее несколько раз, прежде чем пользователь отклонит предупреждение, она добавит новый текст предупреждения в предупреждение, которое уже представлено. . Если отображается другое представление, предупреждение не появится. Не все согласятся с таким поведением, но оно хорошо работает в простых ситуациях.

extension UIViewController {
    func showAlert(_ msg: String, title: String = "") {
        if let currentAlert = self.presentedViewController as? UIAlertController {
            currentAlert.message = (currentAlert.message ?? "") + "\n\nUpdate:\(title): \(msg)"
            return
        }

        // create the alert
        let alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))

        // show the alert
        self.present(alert, animated: true, completion: nil)
    }
}
биомикер
источник
Хорошо, это то, что мне нужно. Это работает и в iOS 13.
Золтан Винклер
3

Мы можем просто проверить, присутствует ли какой-либо контроллер представления.

если представлен, проверьте, является ли это типом UIAlertController.

    id alert = self.presentedViewController;

    if (alert && [alert isKindOfClass:[UIAlertController class]]) 
      {
           *// YES UIAlertController is already presented*
      }
    else
       {
        // UIAlertController is not presented OR visible.
       }
Ravi
источник
1

вы можете проверить - в одной строке - если предупреждение уже представлено:

if self.presentedViewController as? UIAlertController != nil {
    print ("alert already presented")
}
Тьерри Г.
источник
Вы можете объяснить код в своем ответе. Или как он добавляет релевантную информацию, когда уже есть принятый или высоко оцененный ответ. Прочтите, как написать хороший ответ
Леа Грис
0

Я использовал это для обнаружения, удаления и предупреждения.

Сначала мы создаем оповещение со следующей функцией.

 var yourAlert :UIAlertController!

 func useYouAlert (header: String, info:String){


    yourAlert = UIAlertController(title:header as String, message: info as String, preferredStyle: UIAlertControllerStyle.alert)



    let okAction = UIAlertAction(title: self.langText[62]as String, style: UIAlertActionStyle.default) { (result : UIAlertAction) -> Void in
        print("OK") 

    }


    yourAlert.addAction(okAction)
    self.present(yourAlert.addAction, animated: true, completion: nil)

}

И в какой-то другой части вашего кода

    if yourAlert != nil {

      yourAlert.dismiss(animated: true, completion: nil)

    }
Надежда
источник
0

Для последнего языка Swift вы можете использовать следующее:

var alert = presentedViewController

if alert != nil && (alert is UIAlertController) {
    // YES UIAlertController is already presented*
} else {
    // UIAlertController is not presented OR visible.
}
Шахид Аслам
источник
0

Отключите текущий контроллер и представьте контроллер предупреждений, например

 func alert(_ message:String) {
  let alert = UIAlertController(title: "Error!", message: message, preferredStyle: .alert)
  alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil))
  self.dismiss(animated: false, completion: nil)
  self.present(alert, animated: true,completion: nil)
    }
Файз Уль Хассан
источник
0

Swift 4.2+ Ответ

if UIApplication.topViewController()!.isKind(of: UIAlertController.self) { 
            print("UIAlertController is presented")}

Для тех, кто не знает, как получить самый лучший Viewcontroller

extension UIApplication {


public class func topViewController(_ base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
    if let nav = base as? UINavigationController {
        return topViewController(nav.visibleViewController)
    }
    if let tab = base as? UITabBarController {
        if let selected = tab.selectedViewController {
            return topViewController(selected)
        }
    }
    if let presented = base?.presentedViewController {
        return topViewController(presented)
    }
    return base
}}

Swift 5+ Answer 'keyWindow' устарел в iOS 13.0. Предлагаемое изменение

if UIApplication.topViewController()!.isKind(of: UIAlertController.self) { 
            print("UIAlertController is presented")}

Для тех, кто не знает, как получить самый лучший Viewcontroller

extension UIApplication {


public class func topViewController(_ base: UIViewController? = UIApplication.shared.windows.first?.rootViewController) -> UIViewController? {
    if let nav = base as? UINavigationController {
        return topViewController(nav.visibleViewController)
    }
    if let tab = base as? UITabBarController {
        if let selected = tab.selectedViewController {
            return topViewController(selected)
        }
    }
    if let presented = base?.presentedViewController {
        return topViewController(presented)
    }
    return base
}}
iOS Lifee
источник
0

Я обнаружил, что мне нужно создать очередь для стекирования запросов UIAlertController.

NSMutableArray *errorMessagesToShow; // in @interface
errorMessagesToShow=[[NSMutableArray alloc] init];  // in init

-(void)showError:(NSString *)theErrorMessage{
    if(theErrorMessage.length>0){
        [errorMessagesToShow addObject:theErrorMessage];
        [self showError1];
    }
}
-(void)showError1{
    NSString *theErrorMessage;
    if([errorMessagesToShow count]==0)return; // queue finished

    UIViewController* parentController =[[UIApplication sharedApplication]keyWindow].rootViewController;
    while( parentController.presentedViewController &&
      parentController != parentController.presentedViewController ){
        parentController = parentController.presentedViewController;
    }
    if([parentController isKindOfClass:[UIAlertController class]])return;  // busy

    // construct the alert using [errorMessagesToShow objectAtIndex:0]
    //  add to each UIAlertAction completionHandler [self showError1];
    //   then

    [errorMessagesToShow removeObjectAtIndex:0];
    [parentController presentViewController:alert animated:YES completion:nil]; 
}
Питер Б. Крамер
источник
-3

Просто отклоните текущий контроллер и представьте тот, который вы хотите, т.е.

self.dismiss(animated: false, completion: nil)

self.displayAlertController()

Идельфонсо Гутьеррес
источник