Reputation: 868
I have a UIAlertController with two buttons and a text field. When the save button is tapped, a segue is performed. I have also set the return key to do this in textFieldShouldReturn
. However, when the return key is tapped, the alert controller does not dismiss. It moves up to the top left corner of the view and causes the animation to hang when going back to the view. I have also tried to dismiss the alert controller using myAlertController.dismiss(animated: false, completion: nil)
, but this throws the "Attempt to present a UINavigationController on a UIAlertController whose view is not in the window hierarchy" error. How can I dismiss the alert controller when the return key is tapped and still perform a segue? Thanks.
import UIKit
import CoreData
var albumNameFromTextfield: String = ""
class AlbumViewController: UIViewController, UITextFieldDelegate {
// MARK: - Properties
@IBOutlet weak var albumCollectionView: UICollectionView!
@IBOutlet weak var noAlbumsView: UIView!
var albums: [NSManagedObject] = []
let newAlbumAlert = UIAlertController(title: "Create New Album", message: "Enter a name for your new album.", preferredStyle: UIAlertControllerStyle.alert)
// MARK: - Actions
@IBAction func unwindToAlbumsScreen(sender: UIStoryboardSegue) {
}
@IBAction func newAlbumTapped(_ sender: UIBarButtonItem) {
createNewAlbum()
}
func createNewAlbum() {
let nAA = newAlbumAlert
present(nAA, animated: true, completion: nil)
}
// Alert controller setup
func createNewAlbumAlert() {
let saveAction = UIAlertAction(title: "Save", style: .default, handler: { (action) in
guard let textField = self.newAlbumAlert.textFields?[0],
let nameToSave = textField.text else {
return
}
print("Album name has been inputted; save button tapped.")
self.save(name: nameToSave)
self.albumCollectionView.reloadData()
self.performSegue(withIdentifier: "showNewAlbumViewController", sender: self)
})
newAlbumAlert.addTextField{ (textField: UITextField) in
textField.placeholder = "Album Name"
textField.keyboardType = .default
textField.autocorrectionType = .default
textField.returnKeyType = .done
textField.delegate = self
textField.enablesReturnKeyAutomatically = true
}
newAlbumAlert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
newAlbumAlert.addAction(saveAction)
}
//Text field functions
func textFieldDidBeginEditing(_ textField: UITextField) {
self.newAlbumAlert.actions[1].isEnabled = false
textField.text = ""
}
func textFieldDidEndEditing(_ textField: UITextField) {
albumNameFromTextfield = textField.text!
print("Album name for nav title set.")
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let TFNameToSave = textField.text
self.save(name: TFNameToSave!)
self.albumCollectionView.reloadData()
textField.resignFirstResponder()
self.performSegue(withIdentifier: "segueForTF", sender: self)
print("Album name has been inputted; return button tapped.")
return true
}
@objc func editingChanged(_ textField: UITextField) {
if textField.text?.count == 1 {
if textField.text?.first == " " {
textField.text = ""
return
}
}
guard
let alertControllerText = newAlbumAlert.textFields![0].text, !alertControllerText.isEmpty
else {
newAlbumAlert.actions[1].isEnabled = false
return
}
newAlbumAlert.actions[1].isEnabled = true
}
// ViewDidLoad and ViewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//Core Date functions
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Album")
do {
albums = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
//Setup to do when the view will appear
self.albumCollectionView.reloadData()
if albums.count == 0 {
noAlbumsView.alpha = 1
} else {
noAlbumsView.alpha = 0
}
}
override func viewDidLoad() {
super.viewDidLoad()
createNewAlbumAlert()
newAlbumAlert.textFields![0].addTarget(self, action: #selector(editingChanged), for: .editingChanged)
}
//Core Data functions
func save(name: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Album",
in: managedContext)!
let albumName = NSManagedObject(entity: entity,
insertInto: managedContext)
albumName.setValue(name, forKeyPath: "albumName")
do {
try managedContext.save()
albums.append(albumName)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
Upvotes: 0
Views: 635
Reputation: 599
Instead of calling dismiss
on myAlertController
, try calling dismiss
on your AlbumViewController
. I've edited your textFieldShouldReturn
function below:
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let TFNameToSave = textField.text
self.save(name: TFNameToSave!)
self.albumCollectionView.reloadData()
textField.resignFirstResponder()
// ViewController should dismiss the alert controller
dismiss(animated: true) {
self.performSegue(withIdentifier: "segueForTF", sender: self)
print("Album name has been inputted; return button tapped.")
}
return true
}
From the docs:
The presenting view controller is responsible for dismissing the view controller it presented.
Here, the presenting view controller should be the AlbumViewController
and the presented is myAlertController
.
Hope that works!
Upvotes: 2