当新的 UITextView 被分配为第一响应者时如何防止键盘下降

How to prevent the keyboard from lowering when a new UITextView is assigned as first responder

我有一个 table 视图,它使用带有 UITextView 的自定义单元格。每当用户在单元格的 textView 中编辑文本然后点击 return 时,一个新单元格将插入到填充 table 视图单元格的数据列表中,并且 tableView.reloadData()调用以便新单元格立即显示。当用户按下 return 时正在编辑的单元格的 textView.tag + 1 被存储为一个名为 cellCreatedWithReturn 的变量,如果在 tableView 重新加载时该变量不为 nil,则具有 indexPath.row 的单元格(因此刚刚创建的新单元格)成为第一响应者。

我遇到的问题是,当我点击 return 时,会创建新的单元格并将其指定为第一响应者,但键盘会发出嘶嘶声,因为它开始隐藏起来然后射击后退,而不是原地不动。演示我正在寻找的功能的应用程序将是 Apple 的 Reminders 应用程序。当您点击 return 时,将创建一个新单元格并在该新单元格上开始编辑,但键盘一直保持打开状态而不会发出刺耳的声音。

我尝试的一件事是从我的 shouldChangeTextIn 函数中注释掉 textView.endEditing(true) 以查看这是否是键盘降低的原因,但这并没有导致任何变化。

这是我的 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()
                }
            }
        }

}

首先,在您的单元格 中处理您的文本视图操作 class。然后使用闭包告诉控制器发生了什么。

因此,当用户点击 Return 时:

  • shouldChangeTextIn
  • 截取
  • 使用闭包通知控制器
  • 在您的控制器中,向您的数据结构添加一个元素
  • 使用 .performBatchUpdates() 在 table 视图的下一行插入一个单元格
  • 完成后,将新单元格中的文本视图告诉 .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
    }
    
}