paescebu / CustomKeyboardKit

Creating custom In App Keyboards with SwiftUI has never been easier!
GNU General Public License v3.0
203 stars 20 forks source link

Access textDocumentProxy from TextField code block #9

Closed amoshsueh closed 1 year ago

amoshsueh commented 1 year ago

Hi @paescebu,

Sorry to bother you again.

I want to create a custom number keyboard which has calculator function, so I need to store some states for operator or result.

Is there any way I can access textDocumentProxy from TextField code block?

Code example:

struct NumberTextField: View {
    @Binding var amountText: String
    let placeholder: String?
    let isAmountEditing: FocusState<Bool>.Binding
    let onEditingChanged: (Bool) -> Void

    // Some states for calculation
    @State private var number = Double.zero
    @State private var buttonType = AHNumberButtonType.none
    @State private var operation = AHOperationType.none
    @State private var isInputting = false

    var body: some View {
        TextField("", text: $amountText, onEditingChanged: onEditingChanged)
            .customKeyboard(.numberPad)
            .onInputChanged { textDocumentProxy, submit, playSystemFeedback in
                 // I want to access textDocumentProxy from here and do some calculation
            }
            .multilineTextAlignment(.trailing)
            .focused(isAmountEditing)                           
    }
}

IMG_2522FC2B3F80-1

paescebu commented 1 year ago

Hi @amoshsueh,

Don't worry, no one is bothering anyone here! I'm happy my little library is used. That keyboard above looks gorgeous :)

I'm not entirely sure what you are trying to achieve. But better than the textDocumentProxy would be your amountText. You could be using that one to base your calculations off of.

Alternatively you can send me a stripped down example project that i could look into to figure out how to fulfill your requirement.

paescebu commented 1 year ago

Would that first extension help you to fix your issue?

import SwiftUI
import CustomKeyboardKit

extension TextField {
    func customKeyboard(view: @escaping (UITextDocumentProxy, CustomKeyboardBuilder.SubmitHandler?, CustomKeyboardBuilder.SystemFeedbackHandler?) -> some View) -> some View {
        customKeyboard(CustomKeyboardBuilder(customKeyboardView: view))
    }
}

struct ContentView: View {
    @State var text = "123"

    var body: some View {
        VStack {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            Text("Hello, world!")
            TextField("", text: $text) { editing in
                print(editing)
            }
            .customKeyboard { textDocumentProxy, onSubmit, playFeedback in
                //Your keyboard, defined inline, but able to access all properties from the current View.
                VStack {
                    Button("1") {

                    }
                    Button("2") {

                    }
                }
            }
        }
        .padding()
    }
}
paescebu commented 1 year ago

This allows you to something like this as well:

struct ContentView: View {
    @State var text = "123"
    @State var documentProxy: UITextDocumentProxy? = nil
    @FocusState var isEditing: Bool

    var body: some View {
            VStack {
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                Text("Hello, world!")
                TextField("", text: $text)
                .customKeyboard { textDocumentProxy, onSubmit, playFeedback in
                    //Your keyboard, defined inline, but able to access all properties from the current View.
                    VStack {
                        Button("1") {
                            textDocumentProxy.insertText("1")
                        }
                    }
                    .onAppear {
                        self.documentProxy = textDocumentProxy
                    }
                }
                .focused($isEditing)
                .onChange(of: isEditing) { isEditing in
                    //Access from the textDocumentProxy from outside on change of focus
                    documentProxy?.insertText("Edited")
                }
            }
            .padding()
    }
}

But be aware: to work with the textDocumentProxy makes theoretically only sense if the TextField is focused/editing. If it's not the textDocumentProxy has no effect anymore when you try to change the content.

amoshsueh commented 1 year ago

Wait a minute. You're too efficient for that. My demo project is still only half finished, let me try the method you provided first, it looks like what I want.

paescebu commented 1 year ago

@amoshsueh let me know if i can be of any assistance :)

amoshsueh commented 1 year ago

@paescebu It works perfectly, what a magic code you did! Just bought you some dark hot chocolates, hope you enjoy it.

Have a nice weekend :)

amoshsueh commented 1 year ago

Solved my problem and close this issue.