TonyStark4ever
TonyStark4ever

Reputation: 868

How do I dismiss a UIAlertController when the return key is tapped in Swift?

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.

Note the alert controller goes up to the top left corner

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

Answers (1)

Daven
Daven

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

Related Questions