Use custom InputViewController in app

347 Views Asked by At

My current assignment is an iOS Keyboard Extension. Now, to pass App Store Review, the containing app has to provide some "real" content. I thought about letting the user test the keyboard before enabling it in the Settings. So my storyboard looks like this (reduced to show relevant stuff):

└ ViewController
  └ View
    ├ TextField
    └ Keyboard Container
      └ KeyboardViewController

This works all right, the keyboard shows in the container, but - as the textDocumentProxy object in the keyboard's view controller isn't attached to anything - the user can't see what s/he's typing.

Now I'm looking for a way to "attach" the keyboard to the text field, while keeping the system keyboard out of the way so the user can type on the custom one.

I already managed to get the system keyboard away by attaching the following function to the textfield's editing did begin action.

@IBAction func editingBegan(sender: UITextField) {
    sender.endEditing(true)
}
1

There are 1 best solutions below

0
s3lph On BEST ANSWER

I managed to find the following workaround:

I created a class which implemented the UITextDocumentProxy protocol, which writes to the text field, and set this as an additional proxy in the keyboard view controller:

class ProxyWrapper: NSObject, UITextDocumentProxy {

    private let textField: UITextField

    init(textField: UITextField) {
        self.textField = textField
    }

    var documentContextBeforeInput: String? {
        get {
            return textField.text
        }
    }

    var documentContextAfterInput: String? {
        get {
            return ""
        }
    }

    func adjustTextPositionByCharacterOffset(offset: Int) {}

    func insertText(text: String) {
        let ntext = text.stringByReplacingOccurrencesOfString("\n", withString: "")
        textField.text = (textField.text ?? "") + ntext
    }

    func deleteBackward() {
        if let text = textField.text {
            textField.text = text.substringToIndex(text.endIndex.advancedBy(-1))
        }
    }

    func hasText() -> Bool {
        return textField.hasText()
    }

}

As you can see, I only implemented a very minimalist class (text input/deletion only at the end), as I disabled user interactivity altogether for the text field so no "real" keyboard would ever pop up. Also, I don't allow the insertion of newlines.

It may seem a bit hackish and there may be solutions way better (feel free to tell me), but this works and thus I used it.