3

I am developing a IOS custom keyboard. I was wondering if there was a way to fetch the current text inside of the text field and how it would work.

For example, we can use textDocumentProxy.hasText() to see if the textfield has text inside but I want to know the exact string that is inside the textfield.

Foobar
  • 7,458
  • 16
  • 81
  • 161

1 Answers1

11

The closest things would be textDocumentProxy.documentContextBeforeInput and textDocumentProxy.documentContextAfterInput. These will respect sentences and such, which means if the value is a paragraph, you will only get the current sentence. Users have been known to retrieve the entire string by repositioning the cursor multiple times until everything is retrieved.

Of course, you generally do not have to worry about this if the field expects a single value like a username, email, id number, etc. Combining the values of both before and after input contexts should suffice.

Sample Code

For the single phrase value, you would do:

let value = (textDocumentProxy.documentContextBeforeInput ?? "") + (textDocumentProxy.documentContextAfterInput ?? "")

For values that might contain sentence ending punctuation, it will be a little more complicated as you need to run it on a separate thread. Because of this, and the fact that you have to move the input cursor to get the full text, the cursor will visibly move. It is also unknown whether this will be accepted into the AppStore (after all, Apple probably did not add an easy way to get the full text on purpose in order to prevent official custom keyboards from invading a user's privacy).

Note: the below code is based off of this Stack Overflow answer except modified for Swift, removed unnecessary sleeps, uses strings with no custom categories, and uses a more efficient movement process.

func foo() {
    dispatch_async(dispatch_queue_create("com.example.test", DISPATCH_QUEUE_SERIAL)) { () -> Void in
        let string = self.fullDocumentContext()
    }
}

func fullDocumentContext() {
    let textDocumentProxy = self.textDocumentProxy

    var before = textDocumentProxy.documentContextBeforeInput

    var completePriorString = "";

    // Grab everything before the cursor
    while (before != nil && !before!.isEmpty) {
        completePriorString = before! + completePriorString

        let length = before!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)

        textDocumentProxy.adjustTextPositionByCharacterOffset(-length)
        NSThread.sleepForTimeInterval(0.01)
        before = textDocumentProxy.documentContextBeforeInput
    }

    // Move the cursor back to the original position
    self.textDocumentProxy.adjustTextPositionByCharacterOffset(completePriorString.characters.count)
    NSThread.sleepForTimeInterval(0.01)

    var after = textDocumentProxy.documentContextAfterInput

    var completeAfterString = "";

    // Grab everything after the cursor
    while (after != nil && !after!.isEmpty) {
        completeAfterString += after!

        let length = after!.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)

        textDocumentProxy.adjustTextPositionByCharacterOffset(length)
        NSThread.sleepForTimeInterval(0.01)
        after = textDocumentProxy.documentContextAfterInput
    }

    // Go back to the original cursor position
    self.textDocumentProxy.adjustTextPositionByCharacterOffset(-(completeAfterString.characters.count))

    let completeString = completePriorString + completeAfterString

    print(completeString)

    return completeString
}
Community
  • 1
  • 1
Kevin Low
  • 2,672
  • 16
  • 17
  • Could give some example code to show how this is done? – Foobar Jun 22 '16 at 20:42
  • I added sample code for both situations. Note that the full text way is probably an oversight on Apple's part as I cannot imagine that they would want keyboards to retrieve the full text. But then again, who knows. – Kevin Low Jun 24 '16 at 01:17