Swift:"尝试呈现其视图不在窗口层次结构中的UIAlertController!"

sza*_*ady 3 iphone hierarchy ios swift

我正在尝试在单击按钮时显示UIAlertController(按钮单击运行某些代码,并根据所述结果 - 警报显示).最初的ViewController是默认的,我创建了第二个(ConsoleViewController).用户登录,并在成功登录后,转到下一个视图(ConsoleViewController),该视图显示数据(位于ConsoleViewController的viewDidLoad()部分).一旦用户点击"登记",该应用程序将捕获设备的GPS位置,当前日期/时间,并打开相机拍摄(自拍)照片.在相机中选择"使用照片"(尚未编码该功能)后,它会将所有3个参数发送到API处理程序.

第二个按钮打开日期选择器,用户选择日期和时间.点击"提交"按钮后,将使用所选日期(来自日期选择器)更新label.text,并且会弹出警报,说明基于API处理程序的returnString成功保存日期.

我遇到的问题是,我希望警报弹出窗口显示"成功"或"失败",具体取决于数据是否成功发送(基于API处理程序的returnString).我一直在收到错误Warning: Attempt to present <UIAlertController: 0x7fd03961b0a0> on <appName.ViewController: 0x7fd039538640> whose view is not in the window hierarchy!.我已经尝试将警报视图添加到主线程,我尝试改变segue的呈现方式(推送,模态等),以及我在StackOverFlow上找到的所有其他内容(以及搜索Google),没有解决方案似乎对我有用.我根据不正确的登录凭据(在ViewController上)创建了警报,并且弹出窗口正常工作.

下面是我的代码...不要介意我的随机print行...它可以帮助我跟踪我在哪里哈哈.

注意:我添加了相应的info.plist项目,以显示来自iOS的正确弹出窗口.这些也告诉我他们不在视图层次结构中

    func consoleAlertPopup(title: String, message: String) {

    let alertController = UIAlertController(title: title, message: message,     preferredStyle: UIAlertControllerStyle.alert)
        UIApplication.shared.keyWindow?.rootViewController?.present(alertController,     animated: true, completion: nil)
    alertController.addAction(UIAlertAction(title: "Try Again", style:     UIAlertActionStyle.default, handler:  nil))
}
Run Code Online (Sandbox Code Playgroud)

ConsoleViewController:

import UIKit
import CoreLocation
import MobileCoreServices

class ConsoleViewController: UIViewController, CLLocationManagerDelegate {

var alertView: UIAlertController?

// IB Outlets \\
@IBOutlet var DisplayUserName: UILabel!
@IBOutlet var LastCheckInLabel: UILabel!
@IBOutlet var NextCourtDateLabel: UILabel!
@IBOutlet weak var CourtDateButton: UIButton!

@IBOutlet weak var courtDatePicker: UIDatePicker!

//Global Variables & UI Elements
var checkInImg: UIImage!
var userNameString: String!
var newDisplayDate: String?
var updatedCourtLabel: String?
let formatter = DateFormatter()
let displayFormatter = DateFormatter()
var locationManager: CLLocationManager!

@IBAction func clickCheckIn(_ sender: UIButton) {

    sendPicture()
    //Camera Pop Up

}

@IBAction func clickCourtDate() {

    courtPickerAction(Any.self)

}

@IBAction func courtPickerAction(_ sender: Any) {


   DatePickerDialog().show("Select Next Court Date", doneButtonTitle: "Submit", cancelButtonTitle: "Cancel", datePickerMode: .dateAndTime) {
        (courtDateTime) -> Void in

    if courtDateTime == nil {
        //Do nothing
    } else {
        self.formatter.dateFormat = "yyyy-MM-dd HH:mm"
        self.newDisplayDate = self.formatter.string(from: (courtDateTime)!)


    //print("Date after format: \(courtDateTime)")
    print("Date and time: \(self.newDisplayDate) after sendDefendantData func")


    // Submit Button - Date Picker \\
    if (DatePickerDialog().doneButton != nil) {

        self.sendDefendantData()


        print("Send Defendant Data from Submit")
        print("After sendDefData: \(self.newDisplayDate)")

        self.displayFormatter.dateStyle = DateFormatter.Style.full
        self.displayFormatter.timeStyle = DateFormatter.Style.short
        self.NextCourtDateLabel.text = self.displayFormatter.string(from: courtDateTime!)

            }

        }
    }
}


override func viewDidLoad() {
    super.viewDidLoad()


    print("Console View Did Load")
    self.hideKeyboardWhenTappedAround()

    DisplayUserName.text! = userNameString
    // For location allowance from user
    // I've placed this code here (instead of in a function) so the alert
    // pop up will show and allows accessing location. "not in hierarchy"
    // elsewise.
    self.locationManager = CLLocationManager()
    self.locationManager.delegate = self
    self.locationManager.requestWhenInUseAuthorization()

    // Format Display Date & Times
    self.displayFormatter.dateStyle = DateFormatter.Style.full
    self.displayFormatter.timeStyle = DateFormatter.Style.long


    // Retrieve Defendant Data From API Handler
    getDefendantData()





    // Do any additional setup after loading the view.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}




func presentAlert(_ message: String) {

    self.alertView = UIAlertController(title: "Title", message: "Message", preferredStyle: .alert)
    alertView?.addAction(UIAlertAction(title: "OK", style: .cancel) { _ in })

    ViewController().present(alertView!, animated: true, completion: nil)

}


func consoleAlertPopup(title: String, message: String) {

    let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.alert)
    UIApplication.shared.keyWindow?.rootViewController?.present(alertController, animated: true, completion: nil)
    alertController.addAction(UIAlertAction(title: "Try Again", style: UIAlertActionStyle.default, handler:  nil))
}

func getDefendantData() {...}
func sendDefendantData() {...}
func sendPicture() {....}
Run Code Online (Sandbox Code Playgroud)

视图控制器:

    import UIKit


// Hide Keyboard \\
extension UIViewController {
    func hideKeyboardWhenTappedAround() {
        let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
        view.addGestureRecognizer(tap)
    }

    func dismissKeyboard() {
        view.endEditing(true)
    }
}



class ViewController: UIViewController {

    // Send User Login to Console Screen \\
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if (segue.identifier == "toConsoleScreen") {

            let secondViewController = segue.destination as! ConsoleViewController

            secondViewController.userNameString = UserNameField.text!

            print("PrepareSegue")
        }

    }

    @IBAction func UserNameEditBegan() {
        UserNameField.text = nil
    }
    @IBAction func PasswordEditBegan() {
        PasswordField.text = nil
    }

    @IBOutlet weak var UserNameField: UITextField!
    @IBOutlet weak var PasswordField: UITextField!

    func successfulLogin(Username: String) {


        print("Inside Function")

        print(Username)
        print("Inside Successful Login")

        // Show next view - Add to Main Queue\\
        OperationQueue.main.addOperation{

            //print("Before dismissal")
            // self.dismiss(animated: true, completion: nil)
            //print("After dismissal")
            self.performSegue(withIdentifier: "toConsoleScreen", sender: self)
            print("After segue")

        }
    }



    override func viewDidLoad() {
        super.viewDidLoad()

        self.hideKeyboardWhenTappedAround()



        // Do any additional setup after loading the view, typically from a nib.


    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func loginButton() {

        login(Username: UserNameField.text!, Password: PasswordField.text!) { username in
            self.successfulLogin(Username: username)
    }



}



    }
Run Code Online (Sandbox Code Playgroud)

小智 13

尝试在DispatchQueue中展示您的UIAlertController

DispatchQueue.main.async {

    let alert = UIAlertController(title: "Alert!", message: nil, preferredStyle: .alert)

    let cancelAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)

    alert.addAction(cancelAction)
    self.present(alert, animated: true, completion: nil)   
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ton 2

这行:

ViewController().present(alertView!, animated: true, completion: nil)
Run Code Online (Sandbox Code Playgroud)

创建 的一个新实例ViewController并调用present它的方法。那是行不通的。您需要从本身呈现的视图控制器中调用它。看起来代码就在里面ConsoleViewController,也许你可以直接使用self那里。

  • 听起来好像有两个不同的视图控制器同时呈现。您可能希望将对“requestWhenInUseAuthorization”的调用移至稍后的生命周期方法,例如“viewDidAppear”。 (2认同)