iOS 键盘:textDocumentProxy.documentContextBeforeInput.isEmpty 意外返回 Nil(不应该返回)
iOS Keyboard: textDocumentProxy.documentContextBeforeInput.isEmpty Unexpectedly Returning Nil (when it shouldn't)
我正在开发自定义 iOS 键盘 – 大部分都已就绪,但我在使用删除键时遇到问题,更具体地说,在删除整个单词时遇到问题。
问题是 self.textDocumentProxy.documentContextBeforeInput?.isEmpty returns Nil 即使文本字段中还有剩余字符。
背景介绍:
如果您不熟悉,它在标准 iOS 键盘上的工作方式是在按住退格键的同时,系统一次删除一个字符(前 10 个字符)。 10 个字符后,开始删除整个单词。
在我的代码中,我删除了 10 个单个字符,然后成功删除了几个完整的单词,然后突然 self.textDocumentProxy.documentContextBeforeInput?.isEmpty returns Nil,即使有是文本字段中剩余的字符。
我查看了所有文档和网络,没有看到其他人有同样的问题,所以我确定我遗漏了一些明显的东西,但我感到困惑。
以下是我的 class 定义的相关部分:
class KeyboardViewController: UIInputViewController {
//I've removed a bunch of variables that aren't relevant to this question.
var myInputView : UIInputView {
return inputView!
}
private var proxy: UITextDocumentProxy {
return textDocumentProxy
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
//proxy let proxy = self.textDocumentProxy
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillAppear"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide"), name: UIKeyboardWillHideNotification, object: nil)
self.myInputView.translatesAutoresizingMaskIntoConstraints = true
// Perform custom UI setup here
view.backgroundColor = UIColor(red: 209 / 255, green: 213 / 255, blue: 219 / 255, alpha: 1)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "touchUpInsideLetter:", name: "KeyboardKeyPressedNotification", object: nil)
showQWERTYKeyboard()
}
我为退格按钮设置了侦听器和操作,因为我还在按钮上配置了一堆其他属性。我在 Switch 中这样做——相关部分在这里:
case "<<":
isUIButton = true
normalButton.setTitle(buttonString, forState: UIControlState.Normal)
normalButton.addTarget(self, action: "turnBackspaceOff", forControlEvents: UIControlEvents.TouchUpInside)
normalButton.addTarget(self, action: "turnBackspaceOff", forControlEvents: UIControlEvents.TouchUpOutside)
normalButton.addTarget(self, action: "touchDownBackspace", forControlEvents: UIControlEvents.TouchDown)
let handleBackspaceRecognizer : UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleBackspaceLongPress:")
normalButton.addGestureRecognizer(handleBackspaceRecognizer)
buttonWidth = standardButtonWidth * 1.33
nextX = nextX + 7
感谢您提供任何想法。
**** 修改原始 post 以帮助更清楚地说明问题 ****
以下是旨在创建退格行为的 4 个函数。它们似乎至少对前两个完整单词工作正常,但随后正确建议的可选检查开始评估为 nil 并停止删除。
//Gets called on "Delete Button TouchUpInside" and "Delete Button TouchUpOutside"
func turnBackspaceOff() {
self.backspaceIsPressed = false
keyRepeatTimer.invalidate()
}
//Handles a single tap backspace
func touchDownBackspace() {
(textDocumentProxy as UIKeyInput).deleteBackward()
}
//Handles a long press backspace
func handleBackspaceLongPress(selector : UILongPressGestureRecognizer) {
if selector.state == UIGestureRecognizerState.Began {
self.backspaceIsPressed = true
self.keyRepeatTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "backspaceRepeatHandlerFinal", userInfo: nil, repeats: true)
print("handleBackspaceLongPress.Began")
}
else if selector.state == UIGestureRecognizerState.Ended {
self.backspaceIsPressed = false
keyRepeatTimer.invalidate()
numberOfKeyPresses = 0
print("handleBackspaceLongPress.Ended")
}
else {
self.backspaceIsPressed = false
keyRepeatTimer.invalidate()
numberOfKeyPresses = 0
print("handleBackspaceLongPress. Else")
}
}
func backspaceRepeatHandlerFinal() {
if let documentContext = proxy.documentContextBeforeInput as String? {
print(documentContext)
}
print("backspaceRepeatHandlerFinal is called")
if self.backspaceIsPressed {
print("backspace is pressed")
self.numberOfKeyPresses = self.numberOfKeyPresses + 1
if self.numberOfKeyPresses < 10 {
proxy.deleteBackward()
}
else {
if let documentContext = proxy.documentContextBeforeInput as NSString? {
let tokens : [String] = documentContext.componentsSeparatedByString(" ")
var i : Int = Int()
for i = 0; i < String(tokens.last!).characters.count + 1; i++ {
(self.textDocumentProxy as UIKeyInput).deleteBackward()
}
}
else {
print("proxy.documentContextBeforeInput was nil")
self.keyRepeatTimer.invalidate()
self.numberOfKeyPresses = 0
}
}
}
else {
print("In the outer else")
self.keyRepeatTimer.invalidate()
self.numberOfKeyPresses = 0
}
}
最后,我不完全明白为什么,但是XCode在我创建键盘扩展时自动在下面插入了这两个函数。我对它们进行了一些修改,试图让它发挥作用。
override func textWillChange(textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
super.textWillChange(textInput)
}
override func textDidChange(textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
//let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
super.textDidChange(textInput)
}
让我们描述一下下面这行代码的作用:
if !((self.textDocumentProxy.documentContextBeforeInput?.isEmpty) == nil) {
首先,它需要一个标记为 optional
的对象(由 ?
字母):
let documentContext = self.textDocumentProxy.documentContextBeforeInput
然后,它尝试读取名为 isEmpty
:
的 属性
let isEmpty = documentContext?.isEmpty
然后它计算条件:
if !(isEmpty == nil) {
有两个错误。第一个是您将 Bool
值与 nil
进行比较。另一个是您不确定 documentContext
不是 nil
.
所以,让我们以更合适的方式编写您的代码:
if let documentContext = self.textDocumentProxy.documentContextBeforeInput { // Make sure that it isn't nil
if documentContext.isEmpty == false { // I guess you need false?
// Do what you want with non-empty document context
}
}
我正在开发自定义 iOS 键盘 – 大部分都已就绪,但我在使用删除键时遇到问题,更具体地说,在删除整个单词时遇到问题。
问题是 self.textDocumentProxy.documentContextBeforeInput?.isEmpty returns Nil 即使文本字段中还有剩余字符。
背景介绍: 如果您不熟悉,它在标准 iOS 键盘上的工作方式是在按住退格键的同时,系统一次删除一个字符(前 10 个字符)。 10 个字符后,开始删除整个单词。
在我的代码中,我删除了 10 个单个字符,然后成功删除了几个完整的单词,然后突然 self.textDocumentProxy.documentContextBeforeInput?.isEmpty returns Nil,即使有是文本字段中剩余的字符。
我查看了所有文档和网络,没有看到其他人有同样的问题,所以我确定我遗漏了一些明显的东西,但我感到困惑。
以下是我的 class 定义的相关部分:
class KeyboardViewController: UIInputViewController {
//I've removed a bunch of variables that aren't relevant to this question.
var myInputView : UIInputView {
return inputView!
}
private var proxy: UITextDocumentProxy {
return textDocumentProxy
}
override func canBecomeFirstResponder() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
//proxy let proxy = self.textDocumentProxy
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillAppear"), name: UIKeyboardWillShowNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("keyboardWillHide"), name: UIKeyboardWillHideNotification, object: nil)
self.myInputView.translatesAutoresizingMaskIntoConstraints = true
// Perform custom UI setup here
view.backgroundColor = UIColor(red: 209 / 255, green: 213 / 255, blue: 219 / 255, alpha: 1)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "touchUpInsideLetter:", name: "KeyboardKeyPressedNotification", object: nil)
showQWERTYKeyboard()
}
我为退格按钮设置了侦听器和操作,因为我还在按钮上配置了一堆其他属性。我在 Switch 中这样做——相关部分在这里:
case "<<":
isUIButton = true
normalButton.setTitle(buttonString, forState: UIControlState.Normal)
normalButton.addTarget(self, action: "turnBackspaceOff", forControlEvents: UIControlEvents.TouchUpInside)
normalButton.addTarget(self, action: "turnBackspaceOff", forControlEvents: UIControlEvents.TouchUpOutside)
normalButton.addTarget(self, action: "touchDownBackspace", forControlEvents: UIControlEvents.TouchDown)
let handleBackspaceRecognizer : UILongPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: "handleBackspaceLongPress:")
normalButton.addGestureRecognizer(handleBackspaceRecognizer)
buttonWidth = standardButtonWidth * 1.33
nextX = nextX + 7
感谢您提供任何想法。
**** 修改原始 post 以帮助更清楚地说明问题 **** 以下是旨在创建退格行为的 4 个函数。它们似乎至少对前两个完整单词工作正常,但随后正确建议的可选检查开始评估为 nil 并停止删除。
//Gets called on "Delete Button TouchUpInside" and "Delete Button TouchUpOutside"
func turnBackspaceOff() {
self.backspaceIsPressed = false
keyRepeatTimer.invalidate()
}
//Handles a single tap backspace
func touchDownBackspace() {
(textDocumentProxy as UIKeyInput).deleteBackward()
}
//Handles a long press backspace
func handleBackspaceLongPress(selector : UILongPressGestureRecognizer) {
if selector.state == UIGestureRecognizerState.Began {
self.backspaceIsPressed = true
self.keyRepeatTimer = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: "backspaceRepeatHandlerFinal", userInfo: nil, repeats: true)
print("handleBackspaceLongPress.Began")
}
else if selector.state == UIGestureRecognizerState.Ended {
self.backspaceIsPressed = false
keyRepeatTimer.invalidate()
numberOfKeyPresses = 0
print("handleBackspaceLongPress.Ended")
}
else {
self.backspaceIsPressed = false
keyRepeatTimer.invalidate()
numberOfKeyPresses = 0
print("handleBackspaceLongPress. Else")
}
}
func backspaceRepeatHandlerFinal() {
if let documentContext = proxy.documentContextBeforeInput as String? {
print(documentContext)
}
print("backspaceRepeatHandlerFinal is called")
if self.backspaceIsPressed {
print("backspace is pressed")
self.numberOfKeyPresses = self.numberOfKeyPresses + 1
if self.numberOfKeyPresses < 10 {
proxy.deleteBackward()
}
else {
if let documentContext = proxy.documentContextBeforeInput as NSString? {
let tokens : [String] = documentContext.componentsSeparatedByString(" ")
var i : Int = Int()
for i = 0; i < String(tokens.last!).characters.count + 1; i++ {
(self.textDocumentProxy as UIKeyInput).deleteBackward()
}
}
else {
print("proxy.documentContextBeforeInput was nil")
self.keyRepeatTimer.invalidate()
self.numberOfKeyPresses = 0
}
}
}
else {
print("In the outer else")
self.keyRepeatTimer.invalidate()
self.numberOfKeyPresses = 0
}
}
最后,我不完全明白为什么,但是XCode在我创建键盘扩展时自动在下面插入了这两个函数。我对它们进行了一些修改,试图让它发挥作用。
override func textWillChange(textInput: UITextInput?) {
// The app is about to change the document's contents. Perform any preparation here.
super.textWillChange(textInput)
}
override func textDidChange(textInput: UITextInput?) {
// The app has just changed the document's contents, the document context has been updated.
var textColor: UIColor
//let proxy = self.textDocumentProxy
if proxy.keyboardAppearance == UIKeyboardAppearance.Dark {
textColor = UIColor.whiteColor()
} else {
textColor = UIColor.blackColor()
}
super.textDidChange(textInput)
}
让我们描述一下下面这行代码的作用:
if !((self.textDocumentProxy.documentContextBeforeInput?.isEmpty) == nil) {
首先,它需要一个标记为 optional
的对象(由 ?
字母):
let documentContext = self.textDocumentProxy.documentContextBeforeInput
然后,它尝试读取名为 isEmpty
:
let isEmpty = documentContext?.isEmpty
然后它计算条件:
if !(isEmpty == nil) {
有两个错误。第一个是您将 Bool
值与 nil
进行比较。另一个是您不确定 documentContext
不是 nil
.
所以,让我们以更合适的方式编写您的代码:
if let documentContext = self.textDocumentProxy.documentContextBeforeInput { // Make sure that it isn't nil
if documentContext.isEmpty == false { // I guess you need false?
// Do what you want with non-empty document context
}
}