Swift: "Attempt to present UIAlertController whose view is not in the window hierarchy!"
Swift: "Attempt to present UIAlertController whose view is not in the window hierarchy!"
我正在尝试在单击按钮时显示 UIAlertController(单击按钮会运行特定代码,并根据上述结果显示警报)。最初的 ViewController 是默认的,我创建了第二个 (ConsoleViewController)。用户登录,并在成功登录后转到下一个视图 (ConsoleViewController),该视图显示数据(位于 ConsoleViewController 的 viewDidLoad() 部分)。一旦用户点击 "Check In",应用程序就会捕获设备的 GPS 位置,即当前 date/time,并打开相机拍摄(自拍)照片。在相机中选择 "Use Photo" 后(尚未对该功能进行编码),它将所有 3 个参数发送到 API 处理程序。
第二个按钮打开日期选择器,用户选择日期和时间。点击 "Submit" 按钮后,label.text 会更新为所选日期(来自日期选择器),并且会弹出一个警报,说明日期已根据 [=42] 的 returnString 成功保存=]处理程序。
我遇到的问题是,我想要一个警报弹出窗口显示 "Success" 或 "Failed" 取决于数据是否成功发送(基于 returnString 的API 处理程序)。我一直收到错误 Warning: Attempt to present <UIAlertController: 0x7fd03961b0a0> on <appName.ViewController: 0x7fd039538640> whose view is not in the window hierarchy!
。我已经尝试将警报视图添加到主线程,我已经尝试更改 segue 的呈现方式(推送、模态等),以及我在 Whosebug 上可以找到的几乎所有其他内容(以及搜索 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))
}
控制台ViewController:
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() {....}
ViewController:
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)
}
}
}
这一行:
ViewController().present(alertView!, animated: true, completion: nil)
创建 ViewController
的新实例并调用其上的 present
方法。那行不通的。您需要从本身呈现的视图控制器调用它。看起来该代码在 ConsoleViewController
中,也许您可以在那里使用 self
。
尝试在 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)
}
我正在尝试在单击按钮时显示 UIAlertController(单击按钮会运行特定代码,并根据上述结果显示警报)。最初的 ViewController 是默认的,我创建了第二个 (ConsoleViewController)。用户登录,并在成功登录后转到下一个视图 (ConsoleViewController),该视图显示数据(位于 ConsoleViewController 的 viewDidLoad() 部分)。一旦用户点击 "Check In",应用程序就会捕获设备的 GPS 位置,即当前 date/time,并打开相机拍摄(自拍)照片。在相机中选择 "Use Photo" 后(尚未对该功能进行编码),它将所有 3 个参数发送到 API 处理程序。
第二个按钮打开日期选择器,用户选择日期和时间。点击 "Submit" 按钮后,label.text 会更新为所选日期(来自日期选择器),并且会弹出一个警报,说明日期已根据 [=42] 的 returnString 成功保存=]处理程序。
我遇到的问题是,我想要一个警报弹出窗口显示 "Success" 或 "Failed" 取决于数据是否成功发送(基于 returnString 的API 处理程序)。我一直收到错误 Warning: Attempt to present <UIAlertController: 0x7fd03961b0a0> on <appName.ViewController: 0x7fd039538640> whose view is not in the window hierarchy!
。我已经尝试将警报视图添加到主线程,我已经尝试更改 segue 的呈现方式(推送、模态等),以及我在 Whosebug 上可以找到的几乎所有其他内容(以及搜索 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))
}
控制台ViewController:
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() {....}
ViewController:
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)
}
}
}
这一行:
ViewController().present(alertView!, animated: true, completion: nil)
创建 ViewController
的新实例并调用其上的 present
方法。那行不通的。您需要从本身呈现的视图控制器调用它。看起来该代码在 ConsoleViewController
中,也许您可以在那里使用 self
。
尝试在 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)
}