0
votes

Every post I've seen wants to dismiss the keyboard once user is finished editing a UITextField. I find it annoying to have to tap in the UITextField each time I want to begin editing again to get the keyboard to reappear. I need to keep the keyboard up at all times so the next user input begins as soon as typing starts.

Currently my code can only act on user keyboard input if the UITextField resignFirstResponder method is called in the textFieldShouldReturn -> true UITextFieldDelegate method.

I tried the following method:

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { false }

//
//  TestViewController.swift
//  Hyperpolyglot
//
//  Created by Alan Dripps on 10/02/2019.
//  Copyright © 2019 Alan Dripps. All rights reserved.
//

import UIKit
import AVFoundation

class TestViewController: UIViewController, UITextFieldDelegate {

    @IBOutlet weak var stackView: UIStackView!
    @IBOutlet weak var prompt: UILabel!
    @IBOutlet weak var synthesizeButton: UIButton!
    @IBOutlet weak var instructions: UILabel!
    @IBOutlet weak var englishAnswer: UITextField!

    var words = [String]()
    var testWords = [String]()
    var useHomework: Bool!
    var practiceWrongNoCorrectAnswer: Int!
    var homeworkWrongNoCorrectAnswer: Int!
    var questionCounter = 0
    var showingQuestion = true
    var chosenLanguage = String()
    var language = String()
    let wordsString = "Words"
    var englishWord = String()
    var foreignWord = String()
    var attempted = Int()
    var homeworkAttempted = Int()
    var homework = Int()

    override func viewDidLoad() {
        super.viewDidLoad()

        print("viewDidLoad questionCounter is: \(questionCounter)")
        print("useHomework in viewDidLoad in TestViewController is: \(useHomework!)")

        loadChosenLanguage()
        loadWords()
        print("testWords just before shuffle: \(testWords)")
        testWords.shuffle()
        print("testWords just after shuffle: \(testWords)")

        if useHomework == true {
            navigationItem.title = "Learn Homework"
        } else {
            navigationItem.title = "Learn \(chosenLanguage.capitalized)"
        }
        navigationItem.rightBarButtonItem =
            UIBarButtonItem(title: "Reveal Answer", style: .plain , target: self, action: #selector(answerTapped))

        stackView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
        stackView.alpha = 0

        synthesizeButton.isEnabled = false
        synthesizeButton.alpha = 0.25
        englishAnswer.delegate = self
        englishAnswer.layer.cornerRadius = 0.05 * englishAnswer.bounds.size.width
        let placeholderColor = UIColor.systemGray
        englishAnswer.attributedPlaceholder = NSAttributedString(string: "Tap here to answer", attributes: [NSAttributedString.Key.foregroundColor : placeholderColor])
        askQuestion()

        instructions.isHidden = true
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        //loadWords()
        //print("testWords just before shuffle: \(testWords)")
        //testWords.shuffle()
        //print("testWords just after shuffle: \(testWords)")
        //print("questionCounter in viewWillAppear in TestViewController is: \(questionCounter)")
        englishAnswer.delegate = self
        englishAnswer.returnKeyType = .done
    }

    func textFieldShouldReturn(_ englishAnswer: UITextField) -> Bool {
        //englishAnswer.resignFirstResponder()
        self.englishAnswer(englishAnswer)
        return true
    }

    @IBAction func synthesizeButton(_ sender: UIButton) {
        let utterance = AVSpeechUtterance(string: testWords[questionCounter].components(separatedBy: "::")[0])
        utterance.voice = AVSpeechSynthesisVoice(language: "en-US")
        utterance.rate = 0.5

        let synthesizer = AVSpeechSynthesizer()
        synthesizer.speak(utterance)
    }

    @IBAction func englishAnswer(_ sender: UITextField) {
        print("Entered englishAnswer")
        let trimmed = sender.text?.trimmingCharacters(in: .whitespacesAndNewlines)
        print("trimmed?capitalized in englishAnswer in TestViewController is: \(trimmed?.capitalized ?? "")")
        if trimmed?.capitalized == testWords[questionCounter].components(separatedBy: "::")[0] {
            resetWordCounters()
            if questionCounter + 1 < testWords.count {
                questionCounter += 1
            } else {
                questionCounter = 0
            }

            if case prompt.textColor = UIColor(red: 0, green: 0.7, blue: 0, 
                    alpha: 1) {
                print("prompt.textColor in englishAnswer in 
                TestViewController is: \(prompt.textColor!)")
            }
            DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
                self.prepareForNextQuestion()
            }
        } else {
            appendPracticeWord()
            prompt.textColor = UIColor(red: 0.7, green: 0, blue: 0, alpha: 1)
            synthesizeButton.isEnabled = true
            synthesizeButton.alpha = 1
            instructions.isHidden = false
        }
        englishAnswer.text?.removeAll()
        englishAnswer.placeholder = nil
        englishAnswer.isEnabled = false
        englishAnswer.layer.borderColor = UIColor.lightGray.cgColor as CGColor
    }

    func loadChosenLanguage() {
        if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
            if let savedChosenLanguage = defaults.object(forKey: "languageChosen") as? String {
                print("savedChosen Language in loadChosenLanguage in TestViewController is: \(savedChosenLanguage)")
                chosenLanguage = savedChosenLanguage
            }
        }
    }

    func loadWords() {
        print("in loadWords in TestViewController")
        if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
            print("after defaults in loadWords in TestViewController")
            if var savedWords = defaults.object(forKey: "words") as? [String] {
                words.removeAll()
                testWords.removeAll()
                words = savedWords
                print("savedWords in loadWords in TestViewcontroller are: \(savedWords)")
                print("words in loadWords in TestViewController are: \(words)")
                for savedWord in savedWords {
                    let split = savedWord.components(separatedBy: "::")
                    if useHomework == false {
                        if split[7] == chosenLanguage {
                            testWords.append(savedWord)
                            print("testWords.append from chosenLanguage in loadWords in TestViewController are: \(testWords)")
                        }
                    } else {
                        if split[6] == "1" {
                            testWords.append(savedWord)
                            print("testWords.append from homework in loadWords in TestViewController are: \(testWords)")
                        }
                    }
                }
                savedWords.removeAll()
            }
        }
    }

    override func viewDidLayoutSubviews() {
        //configureButtons()
    }

    func configureButtons() {
        synthesizeButton.layer.cornerRadius = 0.5 * synthesizeButton.bounds.size.width
        synthesizeButton.layer.borderColor = UIColor.lightGray.cgColor as CGColor
        synthesizeButton.layer.borderWidth = 4.0
        synthesizeButton.clipsToBounds = true
    }

    func resetWordCounters() {
        print("questionCounter in resetWordCounters in TestViewController is: \(questionCounter)")
        let resetCountersWord = testWords[questionCounter].components(separatedBy: "::")[1]
        print("resetCountersWord in resetWordCounters in TestViewController is: \(resetCountersWord)")
        if useHomework == false {
            attempted += 1
        } else {
            homeworkAttempted += 1
        }
        if useHomework == false {
            practiceWrongNoCorrectAnswer = 0
        } else {
            homeworkWrongNoCorrectAnswer = 0
        }

        var indexForTestWord = 0
        for testWord in testWords {
            if testWord.components(separatedBy: "::")[1] == resetCountersWord {
                print("testWord equals resetCountersWord: \(resetCountersWord)")
                let split = testWord.components(separatedBy: "::")
                let firstWord = split[0]
                let secondWord = split[1]
                testWords.remove(at: indexForTestWord)
                print("testWords.remove in resetWordCounters in TestViewController are: \(testWords)")
            testWords.insert("\(firstWord)"+"::"+"\(secondWord)"+"::"+"\(practiceWrongNoCorrectAnswer!)"+"::"+"\(homeworkWrongNoCorrectAnswer!)"+"::"+"\(attempted)"+"::"+"\(homeworkAttempted)"+"::"+"\(homework)"+"::"+"\(language)", at: indexForTestWord)
                print("testWords.insert in resetWordCounters in TestViewController: \(testWords)")
                break
            }
            indexForTestWord += 1
        }
        print("indexForTestWord in resetWordCounters in TestViewController is: \(indexForTestWord)")

        removeInsertWord(resetCountersWordPracticeWord: resetCountersWord)
    }

    func appendPracticeWord() {
        if useHomework == false {
            attempted += 1
        } else {
            homeworkAttempted += 1
        }
        if useHomework == false {
            practiceWrongNoCorrectAnswer += 1
        } else {
            homeworkWrongNoCorrectAnswer += 1
        }

        print("questionCounter in appendPracticeWord in TestViewController is: \(questionCounter)")

        let practiceWord = testWords[questionCounter].components(separatedBy: "::")[1]
        print("practiceWord in appendPracticeWord in TestViewController is: \(practiceWord)")

        var indexForPracticeWord = 0
        for testWord in testWords {
            if testWord.components(separatedBy: "::")[1] == practiceWord {
                print("testWord equals practiceWord: \(practiceWord)")
                let split = testWord.components(separatedBy: "::")
                let firstWord = split[0]
                let secondWord = split[1]
                testWords.remove(at: indexForPracticeWord)
                print("testWords.remove in appendPracticeWord in TestViewController: \(testWords)")
            testWords.insert("\(firstWord)"+"::"+"\(secondWord)"+"::"+"\(practiceWrongNoCorrectAnswer!)"+"::"+"\(homeworkWrongNoCorrectAnswer!)"+"::"+"\(attempted)"+"::"+"\(homeworkAttempted)"+"::"+"\(homework)"+"::"+"\(language)", at: indexForPracticeWord)
                print("testWords.insert in appendPracticeWord in TestViewController: \(testWords)")
                break
            }
            indexForPracticeWord += 1
        }

        print("indexForPracticeWord in appendPracticeWord in TestViewController is: \(indexForPracticeWord)")

        removeInsertWord(resetCountersWordPracticeWord: practiceWord)
    }

    func removeInsertWord(resetCountersWordPracticeWord: String) {
        var indexForWord = 0
        for word in words {
            if word.components(separatedBy: "::")[1] == resetCountersWordPracticeWord {
                print("word equals resetCountersWordPracticeWord: \(word)")
                let split = word.components(separatedBy: "::")
                let firstWord = split[0]
                let secondWord = split[1]
                words.remove(at: indexForWord)
                print("words.remove in removeInsertWord in TestViewController are: \(words)")
            words.insert("\(firstWord)"+"::"+"\(secondWord)"+"::"+"\(practiceWrongNoCorrectAnswer!)"+"::"+"\(homeworkWrongNoCorrectAnswer!)"+"::"+"\(attempted)"+"::"+"\(homeworkAttempted)"+"::"+"\(homework)"+"::"+"\(language)", at: indexForWord)
                print("words.insert in removeInsertWord in TestViewController after insert: \(words)")
                break
            }
            indexForWord += 1
        }

        print("indexForWord in removeInsertWord in TestViewController is: \(indexForWord)")

        saveWords()
    }

    @objc func answerTapped() {
        showEnglishQuestion()
    }

    func showEnglishQuestion() {
        showingQuestion = !showingQuestion
        if showingQuestion {
            // we should be showing the question – reset!
            prepareForNextQuestion()
            navigationItem.rightBarButtonItem =
                UIBarButtonItem(title: "Reveal Answer", style: .plain , target: self, action: #selector(answerTapped))

            //englishAnswer.isEnabled = true
        } else {
            // we should be showing the answer – show it now, and set the color to be green
            print("questionCounter before prompt.text in showEnglishQuestion in TestViewController is: \(questionCounter)")
            prompt.text = testWords[questionCounter].components(separatedBy: "::")[0]
            prompt.textColor = UIColor(red: 0, green: 0.7, blue: 0, alpha: 1)
            print("testWords.count in askQuestion in TestViewController is: \(testWords.count)")
            if questionCounter + 1 < testWords.count {
                let showNextLanguage = testWords[questionCounter + 1] .components(separatedBy: "::")[7]
                navigationItem.rightBarButtonItem =
                    UIBarButtonItem(title: "Next \(showNextLanguage.capitalized) Word", style: .plain , target: self, action: #selector(answerTapped))
                print("showNextLanguage in showEnglishQuestion in TestViewController is: \(showNextLanguage)")
                // move the question counter one place
                questionCounter += 1
                print("questionCounter in showEnglishQuestion in TestViewController is: \(questionCounter)")
            } else {
                // wrap it back to 0 if we've gone beyond the size of the array
                questionCounter = 0
                print("questionCounter in questionCounter = 0 else statement in showEnglishQuestion in TestViewController is: \(questionCounter)")
                print("words array in showEnglishQuestion in TestViewController when questionCounter = 0 is: \(testWords)")
                let showNextLanguage = testWords[questionCounter] .components(separatedBy: "::")[7]
                navigationItem.rightBarButtonItem =
                    UIBarButtonItem(title: "Next \(showNextLanguage.capitalized) Word", style: .plain , target: self, action: #selector(answerTapped))
                print("showNextLanguage in if questionCounter == 0 in showEnglishQuestion in TestViewController is: \(showNextLanguage)")
                // move the question counter one place
            }
            englishAnswer.isEnabled = false
        }
        synthesizeButton.isEnabled = false
        synthesizeButton.alpha = 0.25
        instructions.isHidden = true
    }

    func askQuestion() {
        // pull out the foreign word at the current question position
        print("questionCounter before prompt.text in askQuestion in TestViewController is: \(questionCounter)")
        prompt.text = testWords[questionCounter].components(separatedBy: "::")[1]
        print("testWords[questionCounter] in askQuestion in TestViewController is: \(testWords[questionCounter].components(separatedBy: "::")[0])")

        let animation = UIViewPropertyAnimator(duration: 0.5, dampingRatio: 0.5) {
            self.stackView.alpha = 1
            self.stackView.transform = CGAffineTransform.identity
        }
        animation.startAnimation()

        englishAnswer.isEnabled = true
        englishAnswer.layer.borderColor = UIColor.black.cgColor as CGColor

        englishWord = testWords[questionCounter].components(separatedBy: "::")[0]
        foreignWord = testWords[questionCounter].components(separatedBy: "::")[1]
        practiceWrongNoCorrectAnswer = Int(testWords[questionCounter].components(separatedBy: "::")[2])!
        homeworkWrongNoCorrectAnswer = Int(testWords[questionCounter].components(separatedBy: "::")[3])!
        attempted = Int(testWords[questionCounter].components(separatedBy: "::")[4])!
        homeworkAttempted = Int(testWords[questionCounter].components(separatedBy: "::")[5])!
        homework = Int(testWords[questionCounter].components(separatedBy: "::")[6])!
        language = testWords[questionCounter].components(separatedBy: "::")[7]
    }

    func prepareForNextQuestion() {
        let animation = UIViewPropertyAnimator(duration: 0.5, curve: .easeInOut) { [unowned self] in
            self.stackView.transform = CGAffineTransform(scaleX: 0.8, y: 0.8)
            self.stackView.alpha = 0
        }

        animation.addCompletion { [unowned self] position in
            self.prompt.textColor = UIColor.black
            self.askQuestion()
        }

        animation.startAnimation()

        englishAnswer.placeholder = "Tap here to answer"
    }

    func saveWords() {
        if let defaults = UserDefaults(suiteName: "group.co.uk.tirnaelectronics.hyperpolyglot.todayview") {
            defaults.set(words, forKey: "words")
        }
    }

    /*
     // MARK: - Navigation

     // In a storyboard-based application, you will often want to do a little preparation before navigation
     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
     // Get the new view controller using segue.destination.
     // Pass the selected object to the new view controller.
     }
     */
}

The textFieldShouldEndEditing method stopped the keyboard dismissing, but didn't pass on the user's edit to my sender: UITextField method that does something useful with the input.

2
you can catch the same info in textFieldShouldReturn functionLu_
If you just remove all the resignFirstResponder, keyboard will not disappear. Need however to propose a way to hide keyboard at the end.claude31
@Lu_ You mean capture the user input and deal with it like I do in the method: IBAction func englishAnswer(_ sender: UITextField)? I don't see how, as there's no _sender: UITextField paramater in textFieldShouldReturn.Tirna
@claude31 That's correct, but like I said before, nothing happens with the user's input, it doesn't end up in the IBAction func englishAnswer(_ sender: UITextField) method!Tirna
Nothing happens ? Are you sure the func is called ? Add a print("Entered englishAnswer") as first statement to check. What is englishAnswer IBAction connected to ? Is it connected to a sent event ? Which ?claude31

2 Answers

0
votes

Please add textfield's delegate to view controller in storyboard or set "englishAnswer.delegate = self" in viewDidLoad

Don't try return false in textFieldShouldReturn. It won't make any change.

0
votes

Now I got your point clearly. All you have done is correct. Then manually call the method "self.englishAnswer (_ sender: textField) in textFieldShouldEndEditing or textFieldShouldReturn. Then it will work. You haven't call the method anywhere.