不要在 UITextField 返回时关闭键盘

Posted

技术标签:

【中文标题】不要在 UITextField 返回时关闭键盘【英文标题】:Don't dismiss keyboard on UITextField return 【发布时间】:2019-12-28 00:05:49 【问题描述】:

我看到的每个帖子都希望在用户完成 UITextField 编辑后关闭键盘。我发现每次我想再次开始编辑以使键盘重新出现时都必须点击 UITextField 很烦人。我需要始终保持键盘处于打开状态,以便在键入开始后立即开始下一个用户输入。

目前,如果在 textFieldShouldReturn -> true UITextFieldDelegate 方法中调用 UITextField resignFirstResponder 方法,我的代码只能作用于用户键盘输入。

我尝试了以下方法:

func textFieldShouldEndEditing(_ textField: UITextField) -> Bool 错误的

//
//  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.
     
     */

textFieldShouldEndEditing 方法停止键盘关闭,但没有将用户的编辑传递给我的发件人:UITextField 方法对输入有用。

【问题讨论】:

你可以在textFieldShouldReturn函数中捕获相同的信息 如果只是删除所有resignFirstResponder,键盘不会消失。但是需要在最后提出一种隐藏键盘的方法。 @Lu_ 你的意思是捕获用户输入并像我在方法中那样处理它:IBAction func englishAnswer(_ sender: UITextField)?我不明白怎么做,因为 textFieldShouldReturn 中没有 _sender: UITextField 参数。 @claude31 没错,但就像我之前说的,用户的输入没有任何反应,它不会出现在 IBAction func englishAnswer(_ sender: UITextField) 方法中! 什么都没发生?你确定调用了函数吗?添加 print("Entered englishAnswer") 作为要检查的第一条语句。 EnglishAnswer IBAction 连接到什么?它是否连接到已发送的事件?哪个? 【参考方案1】:

请将文本字段的委托添加到情节提要中的视图控制器或在 viewDidLoad 中设置“englishAnswer.delegate = self”

不要尝试在 textFieldShouldReturn 中返回 false。它不会有任何改变。

【讨论】:

我在 viewDidLoad 中设置了 englishAnswer.delegate = self 并在 textFieldShouldReturn 中返回 true。这是否应该将用户输入传递给我的方法,该方法对用户输入进行某些操作,因为与其他海报建议一样,我没有从应该检查用户正确回答问题的方法中得到任何响应。【参考方案2】:

现在我明白了你的意思。你所做的一切都是正确的。然后在textFieldShouldEndEditing或textFieldShouldReturn中手动调用方法“self.englishAnswer(_sender:textField)。然后就可以了。你还没有在任何地方调用该方法。

【讨论】:

程序对错误/正确答案计数变量的正确或错误答案做出正确响应,但不得执行englishAnswer中的prompt.textColor行,因为正确答案应该变成绿色但不是不。程序进入 prompt.textColor 所在的条件测试,但 print test 语句似乎没有打印任何内容来调试 prompt.textColor 的控制台。提交输入后键盘仍然会退出,并且每次用户想要调出键盘回答新问题时都必须点击 UITextField。来源列表包含您的建议。

以上是关于不要在 UITextField 返回时关闭键盘的主要内容,如果未能解决你的问题,请参考以下文章

UITextField 委托返回多个文本字段

当用户在 UITextField 之外点击时关闭键盘 [重复]

iOS - 在 UITextField 之外触摸时关闭键盘

UITextField 键盘不关闭

UITextField 和 UIButton 不可点击

显示键盘时不要滚动collectionview