当新的 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
}
}
我有一个 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
}
}