Как предотвратить опускание клавиатуры, когда новый UITextView назначается в качестве первого ответчика

У меня есть табличное представление, в котором используются пользовательские ячейки с UITextViews. Всякий раз, когда пользователь редактирует текст в текстовом представлении ячейки, а затем нажимает «Ввод», новая ячейка вставляется в список данных, которые заполняют ячейки табличного представления, и вызывается tableView.reloadData(), чтобы новая ячейка отображалась немедленно. TextView.tag + 1 ячейки, которая редактировалась, когда пользователь нажал клавишу возврата, сохраняется как переменная с именем cellCreatedWithReturn, и если эта переменная не равна нулю при перезагрузке tableView, ячейка с этим indexPath.row (поэтому новая ячейка который был только что создан) становится первым ответившим.

Проблема, с которой я сталкиваюсь, заключается в том, что когда я нажимаю «Ввод», создается новая ячейка, и она назначается в качестве первого респондента, но клавиатура отключается, потому что она начинает скрываться, а затем стреляет обратно, вместо того, чтобы просто оставаться на месте. Приложением, которое демонстрирует функциональность, которую я ищу, будет приложение Apple Reminders. Когда вы нажимаете «Ввод», создается новая ячейка, и редактирование начинается в этой новой ячейке, но клавиатура остается все время без пауз.

Одна вещь, которую я пробовал, - это закомментировать textView.endEditing(true) из моей функции shouldChangeTextIn, чтобы увидеть, было ли это причиной опускания клавиатуры, но это не привело к каким-либо изменениям.

Вот мои функции shouldChangeTextIn и cellForRowAt:

var cellCreatedWithReturn: Int?

func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if(text == "\n") {
            textView.endEditing(true)
            cellCreatedWithReturn = textView.tag + 1
            if song.lyrics.count == textView.tag || song.lyrics[textView.tag].text != "" {
                let newLyricLine = LyricLine()
                newLyricLine.text = ""
                do {
                    try realm.write {
                        self.song.lyrics.insert(newLyricLine, at: textView.tag)
                        print("Successfully inserted new lyric line in Realm")
                    }
                } catch {
                    print("Error when inserting new lyric line after pressing return")
                }
            }
            tableView.reloadData()
            return false
        } else {
            return true
        }
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "lyricsCell", for: indexPath) as! newNoteTableViewCell
        
        cell.lyricsField.delegate = self
        
        DispatchQueue.main.async {
            if let newCellIndexPath = self.cellCreatedWithReturn {
                if indexPath.row == newCellIndexPath {
                    cell.lyricsField.becomeFirstResponder()
                }
            }
        }

}

person michaelthedeveloper    schedule 09.10.2020    source источник


Ответы (1)


Во-первых, обработайте действия с текстовым представлением внутри вашего класса ячеек. Затем используйте замыкания, чтобы сообщить контроллеру, что произошло.

Итак, когда пользователь нажимает «Возврат»:

  • Перехватить его в shouldChangeTextIn
  • Используйте замыкание для информирования контроллера
  • В вашем контроллере добавьте элемент в вашу структуру данных
  • Используйте .performBatchUpdates(), чтобы вставить ячейку в следующую строку в табличном представлении.
  • По завершении скажите текстовому представлению в новой ячейке .becomeFirstResponder()

Вот очень простой пример:

// simple cell with a text view
class TextViewCell: UITableViewCell, UITextViewDelegate {
    
    var textView = UITextView()
    
    // closure to tell controller Return was tapped
    var returnKeyCallback: (()->())?
    
    // closure to tell controller text was changed (edited)
    var changedCallback: ((String)->())?
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        textView.translatesAutoresizingMaskIntoConstraints = false
        contentView.addSubview(textView)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            
            textView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            textView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            textView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            
            // use lessThanOrEqualTo for bottom anchor to prevent auto-layout complaints
            textView.bottomAnchor.constraint(lessThanOrEqualTo: g.bottomAnchor, constant: 0.0),
            
            textView.heightAnchor.constraint(equalToConstant: 60.0),

        ])
        
        textView.delegate = self
        
        // so we can see the text view frame
        textView.backgroundColor = .yellow
    }
    
    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
        if(text == "\n") {
            returnKeyCallback?()
            return false
        }
        return true
    }
    func textViewDidChange(_ textView: UITextView) {
        let t = textView.text ?? ""
        changedCallback?(t)
    }
        
}

class AnExampleTableViewController: UITableViewController {
    
    // start with one "row" of string data
    var theData: [String] = [ "First row" ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(TextViewCell.self, forCellReuseIdentifier: "TextViewCell")
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return theData.count
    }
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "TextViewCell", for: indexPath) as! TextViewCell
        
        c.textView.text = theData[indexPath.row]
        
        // handle Return key in text view in cell
        c.returnKeyCallback = { [weak self] in
            if let self = self {
                let newRow = indexPath.row + 1
                self.theData.insert("", at: newRow)
                let newIndexPath = IndexPath(row: newRow, section: 0)
                self.tableView.performBatchUpdates({
                    self.tableView.insertRows(at: [newIndexPath], with: .automatic)
                }, completion: { b in
                    guard let c = tableView.cellForRow(at: newIndexPath) as? TextViewCell else { return }
                    c.textView.becomeFirstResponder()
                })
            }
        }
        
        // update data whenever text in cell is changed (edited)
        c.changedCallback = { [weak self] str in
            if let self = self {
                self.theData[indexPath.row] = str
            }
        }
        
        return c
    }
    
}
person DonMag    schedule 09.10.2020
comment
Спасибо еще раз! Обратные звонки были ответом :) - person michaelthedeveloper; 10.10.2020
comment
@michaelthedeveloper - для всех, кто может столкнуться с вашим вопросом ... если это решило вашу проблему, обязательно отметьте ответ как принятый - person DonMag; 13.10.2020