【问题标题】:Attempt to present UIAlertController on View Controller which is already presenting (null) [Swift]尝试在已经呈现的视图控制器上呈现 UIAlertController (null) [Swift]
【发布时间】:2016-12-26 05:08:37
【问题描述】:

我有一个警报视图,我正试图在照片视图上呈现。

照片显示在列表中,可以推送到全屏视图。

照片视图正在以编程方式显示。我认为这是导致问题的原因,因为警报视图试图在已经呈现的(照片)视图之上呈现另一个视图。

警报视图正在尝试显示,但收到此错误:

Warning: Attempt to present <UIAlertController: 0x147d2c6b0>  on <LiveDeadApp.ListViewController: 0x147d614c0> which is already presenting (null)

可能有问题的行是这一行:

 self.present(textPrompt, animated: true, completion: nil)

这是主列表视图

这是截屏时的主列表视图

这是主照片视图

这是主照片视图中的弹出框(通过“i”按钮访问)

在主照片视图上截取屏幕截图时,不会出现警报视图。但是,当设备的方向发生变化时,照片视图会返回列表并显示警报。

这就是我想要做的:

iOS 10 中的 Swift 3

谢谢!

这是列表视图和照片视图的代码:

import UIKit
import Kingfisher
import SKPhotoBrowser

class ListViewCell: UITableViewCell {

@IBOutlet weak var Cellimage: UIImageView!

@IBOutlet weak var cellVenue: UILabel!

@IBOutlet weak var cellLocation: UILabel!

@IBOutlet weak var cellDate: UILabel!
@IBOutlet weak var aiView: UIActivityIndicatorView!
}

class ListViewController: UITableViewController {

var subcategory:Subcategory!

var objects:[[String:String]] = [[String:String]]()

var images = [SKPhotoProtocol]()



override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)


}

override func viewDidLoad() {
    super.viewDidLoad()


    self.tableView.separatorStyle = .none

    self.view.backgroundColor = UIColor.black

    self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]

    navigationController!.navigationBar.barTintColor = UIColor.black

    let requireTextInput = "require text input"
    // add observer for screen shot
    NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil, queue: OperationQueue.main, using:
        { notification in

            self.definesPresentationContext = true

            var inputTextField = UITextField()

            let textPrompt = UIAlertController(title: "Test!", message: "Testing!", preferredStyle: .alert)

            textPrompt.addAction(UIAlertAction(title: "Continue", style: .default, handler: {
                (action) -> Void in
                // if the input match the required text

                let str = inputTextField.text
                if str == requireTextInput {
                    print("right")
                } else {
                    print("wrong")
                }

            }))

            textPrompt.addTextField(configurationHandler: {(textField: UITextField!) in
                textField.placeholder = ""
                inputTextField = textField

            })

            self.present(textPrompt, animated: true, completion: nil)

    })

    if subcategory != nil {
        self.title = subcategory.title
        self.objects = subcategory.photos

        createLocalPhotos()

        self.tableView.reloadData()
    }


}

func createLocalPhotos() {

    for item in objects {
        let photo = SKPhoto.photoWithImageURL(item["url"]!)
        photo.shouldCachePhotoURLImage = true
        images.append(photo)
    }

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return objects.count
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell: ListViewCell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! ListViewCell

    let item = objects[indexPath.row]

    let title = item["title"]
    let location = item["location"]
    let date = item["date"]
    let urlSrt = item["url"]


    cell.cellVenue.text = title
    cell.cellLocation.text = location
    cell.cellDate.text = date

    if let url = URL(string: urlSrt!) {
        cell.aiView.startAnimating()
        cell.Cellimage.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: nil, completionHandler: { (image, error, cacheType, url) in
            cell.aiView.stopAnimating()
        })
    }

    return cell

}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let cell = tableView.cellForRow(at: indexPath) as! ListViewCell

    if(cell.Cellimage.image != nil ) {
        SKPhotoBrowserOptions.displayToolbar = false
        SKPhotoBrowserOptions.displayCounterLabel = false
        SKPhotoBrowserOptions.displayBackAndForwardButton = false
        SKPhotoBrowserOptions.displayAction = false
        SKPhotoBrowserOptions.displayDeleteButton = true
        SKPhotoBrowserOptions.displayHorizontalScrollIndicator = false
        SKPhotoBrowserOptions.displayVerticalScrollIndicator = false
        SKPhotoBrowserOptions.displayStatusbar = false
        SKPhotoBrowserOptions.disableVerticalSwipe = true
        SKPhotoBrowserOptions.bounceAnimation = false
        let browser = ExtendedSKPhotoBrowser(originImage: cell.Cellimage.image!, photos: images, animatedFromView: cell)

        let btnSize = 80//24 * UIScreen.main.scale

        browser.updateCloseButton(UIImage(named: "ic_close_white")!, size: CGSize(width: btnSize, height: btnSize))
        browser.updateDeleteButton(UIImage(named: "ic_info_white")!, size: CGSize(width: btnSize, height: btnSize))
        browser.initializePageIndex(indexPath.row)
        browser.delegate = self
        present(browser, animated: true, completion: {})
        browser.toggleControls()
    }
}

override var prefersStatusBarHidden: Bool {
    get {
        return true
    }
}


var popOverVC:PopUpViewController!
}

extension ListViewController: SKPhotoBrowserDelegate {
func didShowPhotoAtIndex(_ index: Int) {






}

func willDismissAtPageIndex(_ index: Int) {

}

private func willShowActionSheet(photoIndex: Int) {
    // do some handle if you need
}

func didDismissAtPageIndex(_ index: Int) {
}

func didDismissActionSheetWithButtonIndex(_ buttonIndex: Int, photoIndex: Int) {
    // handle dismissing custom actions
}

func removePhoto(_ browser: SKPhotoBrowser, index: Int, reload: (() -> Void)) {
    popOverVC = self.storyboard?.instantiateViewController(withIdentifier: "sbPopUpID") as! PopUpViewController
    popOverVC.photoData = objects[index]

}

func viewForPhoto(_ browser: SKPhotoBrowser, index: Int) -> UIView? {
    return tableView.cellForRow(at: IndexPath(row: index, section: 0))
}
}


open class ExtendedSKPhotoBrowser: SKPhotoBrowser {

open override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent // white statusbar, .default is black
}

open override var prefersStatusBarHidden: Bool {
    return true
}
}

【问题讨论】:

  • 只有当您尝试呈现一个已经呈现的控制器(或呈现任何其他控制器)时才会出现此错误。检查您是否在代码的某处出现两次“UIAlertController”。
  • @KrishnaDattShukla 这是我唯一展示警报控制器的地方。不过,我也展示了一个弹出框控制器。
  • 我认为您同时呈现弹出窗口和警报。这就是产生此错误的原因。关闭弹出框后显示您的警报控制器。
  • @KrishnaDattShukla 我不确定如何执行此操作。我已在我的问题中添加了照片以进行澄清。非常感谢!

标签: ios swift uialertview uialertcontroller


【解决方案1】:

问题其实很简单,你试图在当前呈现的UIAlertController 上显示另一个UIAlertController

那么,如何解决这种情况

  1. 您需要获取在当前视图控制器中使用的所有UIAlertController 的列表。

  2. 您必须检查在当前视图控制器(或其他视图控制器,如果您正在执行异步请求)中显示警报的逻辑。

  3. 当您想在另一个警报之上显示一个警报时,您的代码必须是这样的。

假设 loadingAlert 当前正在屏幕上显示:

self.loadingAlert.dismiss(animated: true, completion: {
     let anotherAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
     let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
     anotherAlert.addAction(okAction)
     self.present(anotherAlert, animated: true, completion: nil)
})

您必须先关闭第一个,然后才能出现下一个。我做出这个答案是为了消除没有按钮的警报以提高效率。

那么,带有操作按钮的警报呢?

当您单击其中一项操作时,它会自动关闭 UIAlertController 上您创建的按钮。

但是,如果您同时显示两个UIAlertControllers,其中包括UIButtons,问题仍然会出现。您需要重新检查每个操作的逻辑,或者您可以在每个操作的处理程序中处理它:

self.connectionErrorAlert.dismiss(animated: true, completion: {
     let anotherAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
     let okAction = UIAlertAction(title: "OK", style: .default, handler: {action in
            let nextAlert = UIAlertController(title: "New One", message: "The Previous one is dismissed", preferredStyle: .alert)
            self.present(nextAlert, animated: true, completion: nil)
     })
     anotherAlert.addAction(okAction)
     self.present(anotherAlert, animated: true, completion: nil)
})

对迈克的答复

DispatchQueue.main.async(execute: {

      if self.presentedViewController == nil {
           print("Alert comes up with the intended ViewController")
           var inputTextField = UITextField()

           let textPrompt = UIAlertController(title: "Test", message: "Testing", preferredStyle: .alert)

           textPrompt.addAction(UIAlertAction(title: "Continue", style: .default, handler: {
               (action) -> Void in
               // if the input matches the required text

               let str = inputTextField.text
               if str == requireTextInput {
                    print("right")
               } else {
                    print("wrong")
               }

           }))

           textPrompt.addTextField(configurationHandler: {(textField: UITextField!) in
                textField.placeholder = ""
                inputTextField = textField

            })
            weakSelf?.present(textPrompt, animated: true, completion: nil)
      } 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 _ : UIAlertController = thePresentedVC as? UIAlertController {
                      print("Alert not necessary, already on the screen !")

              } else {

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

感谢@Luke 回答:https://stackoverflow.com/a/30741496/3378606

【讨论】:

  • 感谢您的彻底回复。我仍然不确定如何去做。我已在我的问题中添加了照片以进行澄清。谢谢!
  • 你当前的问题有多少个控制器?只有一个 listViewController?
  • 只有一个:ListViewController。
  • 如果我没记错的话,你在 ListViewController 中显示 Photos(1,2,3)。当您单击其中一个时,会出现详细信息(假设 1)。当您单击 info(i) 时,会出现弹出框,当您单击“添加到收藏夹”时,会出现警报。我说的对吗?
  • 这是正确的,除了“添加到收藏夹”部分。 “添加到收藏夹”与此问题是分开的。截屏时会显示警报。
【解决方案2】:

嘿,我尝试了一个似乎可行的更简单的解决方案,并且能够在第一个警报之前显示第二个警报,该警报将保留(在用户回答问题之前无需关闭):

if self.presentedViewController==nil{
    self.present(MyAlert, animated: true, completion: nil)
}else{
    self.presentedViewController!.present(MyAlert, animated: true, completion: nil)
}

【讨论】:

  • 这将是一个优雅的简单解决方案,但您最好事先检查presentedViewController != UIAlertViewController。
【解决方案3】:

嘿,就我而言,我遇到了同样的错误。

简单的解决方案

您同时呈现两个警报控制器。

您需要关闭您的第一个警报控制器,然后 XCode 允许您呈现第二个警报控制器。

您可以关闭此代码 -> self.dismiss(animated: true, completion: nil)

像这样 ->

       let alert = UIAlertController(title: "Delete",
                                      message: "Are you sure?",
                                      preferredStyle: .alert)

        alert.addAction(UIAlertAction(title: "Delete", style: .destructive, handler: { [self] _ in

            // TODO: Your Delete Code
            self.dismiss(animated: true, completion: nil)
        }))
        alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)

【讨论】:

  • 是的!多谢,伙计。做和你说的一样的事情。哈哈。
【解决方案4】:

在 ViewDidLoad 中,
使weak 变量,如weak var weakSelf = self

在通知中心,
textPropmt点赞

weak var weakSelf = self
NotificationCenter.default.addObserver(forName: NSNotification.Name.UIApplicationUserDidTakeScreenshot, object: nil, queue: OperationQueue.main, using:
        { notification in
 DispatchQueue.main.async(execute: {
  //create textPrompt here in Main Thread
  weakSelf.present(textPrompt, animated: true, completion: nil)
 })
})

【讨论】:

  • 我应该将所有警报视图的代码放在“ //create textPrompt here in Main Thread”中吗?感谢您的回复!
  • 这就是我在代码中添加注释的原因。 ;) ,继续。
  • 会的,抱歉。仍然收到该错误并且没有弹出窗口。必须加一个“?”在“weakSelf”之后。
【解决方案5】:

Duplicated from my answer here

在我的情况下,我无法将我的放在类覆盖中。所以,这就是我得到的:

let viewController = self // I had viewController passed in as a function,
                          // but otherwise you can do this


// Present the view controller
let currentViewController = UIApplication.shared.keyWindow?.rootViewController
currentViewController?.dismiss(animated: true, completion: nil)

if viewController.presentedViewController == nil {
    currentViewController?.present(alert, animated: true, completion: nil)
} else {
    viewController.present(alert, animated: true, completion: nil)
}

【讨论】:

    【解决方案6】:

    我遇到过这个问题并追查到这个问题。这是一个简单的应用程序,上面有两个按钮。点击第一个按钮会导致“2019-03-05 16:58:04.094541-0500 ReadJason[41100:1610082] 警告:尝试在其上呈现”错误。

    这个问题是由复制按钮 2 来制作按钮 1 引起的。每个按钮都与一个操作(btn1 和 btn2)相关联。当我复制 btn2 制作 btn1 时,与 btn2 的绑定包含在 btn1 的代码中。然后我将绑定添加到 btn1,这导致两个发送事件绑定到 btn1 - 这就是导致错误的原因。

    检查按钮 1 的事件会显示两个操作:screen shot showing the two actions。删除不需要的操作会清除错误。

    screen shot of main.storyboard

    <pre><code>    
    @interface ViewController ()
    @property (weak, nonatomic) IBOutlet UIButton *btn1;
    @property (weak, nonatomic) IBOutlet UIButton *btn2;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
       [super viewDidLoad];
    }
    
    - (IBAction)btn1:(id)sender {
       UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Btn1"
                       message:@"This is Btn1." preferredStyle:UIAlertControllerStyleAlert];
    
       UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];
    
       [alert addAction:defaultAction];
       [self presentViewController:alert animated:YES completion:nil];
    }
    
    - (IBAction)btn2:(id)sender {
       UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Btn2"
                         message:@"This is Btn2." preferredStyle:UIAlertControllerStyleAlert];
    
       UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {}];
    
       [alert addAction:defaultAction];
       [self presentViewController:alert animated:YES completion:nil];
    }
    </code></pre>
    

    【讨论】:

      【解决方案7】:

      我相信,您需要确定是否已经有另一个 AlerViewController 试图在 ViewController 上表示自己。

      至少那是我的问题。

      【讨论】:

        【解决方案8】:
        let alert = UIAlertController(title: "Alert", message: "No saved address found. Please save address first.", preferredStyle: .alert)
                                            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
                                                
                                                self.navigationController?.popViewController(animated: false)
                            
                                            }))
                                            
                                            if self.presentedViewController==nil{
                                                self.present(alert, animated: true, completion: nil)
                                            }else{
                                                self.presentedViewController!.present(alert, animated: true, completion: nil)
                                            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-10-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多