尝试在已经呈现的View Controller上呈现UIAlertController(null)[Swift]

时间:2016-12-26 05:08:37

标签: ios swift uialertview uialertcontroller

我有一个警报视图,我想在照片视图中显示。

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

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

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

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

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

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

这是主要的列表视图 This is the main list view

这是截屏时的主要列表视图 This is the main list view when a screenshot is taken

这是主要的照片视图 This is the main photo view

这是主照片视图中的popover(通过&#34; i&#34;按钮访问) This is the popover in the main photo view (accessed via the "i" button)

在主照片视图上拍摄屏幕截图时,不会发生警报视图。但是,当设备的方向发生变化时,照片视图会返回列表并显示警告。 When a screenshot is taken on the main photo view, no alert view occurs. However, when the device's orientation is changed, the photo view goes back to the list and shows the alert.

这就是我要做的事情: enter image description here

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

6 个答案:

答案 0 :(得分:4)

问题非常简单,您尝试在当前显示的UIAlertController上显示另一个UIAlertController

  

那么,如何解决这种情况

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

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

  3. 当您想在另一个上方显示一个提醒时,您的代码必须如此。

  4. 假设 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上的按钮。

    但是,如果您同时显示两个包含UIAlertController的{​​{1}},则问题仍然存在。您需要重新检查每个逻辑,或者您可以在每个操作的处理程序中处理它:

    UIButton

    答案迈克

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

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:0)

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

在NotificationCenter,
目前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)
 })
})

答案 3 :(得分:0)

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

答案 4 :(得分:0)

我遇到了这个问题,并一直追踪到这个问题。这是一个带有两个按钮的简单应用程序。轻按第一个按钮会导致“ 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>

答案 5 :(得分:0)

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

至少那是我的问题。