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

问题描述 投票:5回答:4

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

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

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

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

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的主要照片视图

这是主照片视图中的弹出窗口(通过“i”按钮访问)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
}
}
ios swift uialertview uialertcontroller
4个回答
7
投票

问题很简单,你试图在当前呈现的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


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

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

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>
© www.soinside.com 2019 - 2024. All rights reserved.