1

I’m wondering how some apps seamlessly integrate custom buttons above the keyboard in iOS apps. You could create a custom keyboard extension, but that takes a lot of effort and requires the user to enable the keyboard. However, some apps manage to accomplish the same without using a keyboard extension. How can this be implemented in the most organized way possible?

An example I can think of is the app Juno, which presents the standard English keyboard (no keyboard extension), but with a custom toolbar of buttons. How might they have implemented this?

Might there be a way to accomplish this in SwiftUI as well?

Juno Keyboard

EDIT:

Another example of a similar sort of thing: Grammarly Keyboard

Ryan Rudes
  • 508
  • 4
  • 11
  • I don't *believe* there's a way to do it with straight SwiftUI -- you'll probably want to be looking at `inputAccessoryView`, and this probably means you'll be dealing with wrapping view's with UIViewRepresentable, etc. But, I'm not leaving this as an answer in case others have better ideas. PS, your battery indicator makes me nervous :) https://stackoverflow.com/questions/35689528/add-a-view-on-top-of-the-keyboard-using-inputaccessoryview-swift – jnpdx Feb 12 '21 at 22:13
  • Is your target iOS 13 or iOS 14? In iOS 14, views can avoid the keyboard. – George Feb 12 '21 at 22:39

1 Answers1

0

Here's a very basic way to set an inputAccessoryView on a UITextField that's been wrapped in UIViewRepresentable to use in SwiftUI:

struct ContentView: View {
    
    @State var text = ""
    
    var body: some View {
        
        VStack {
            Text("My text field:")
            TextFieldWithCustomButtons(text: $text)
                .frame(height: 60)
                .border(Color.red)
            Text(text)
        }
        
    }
}

struct TextFieldWithCustomButtons : UIViewRepresentable {
    @Binding var text: String
    
    func makeUIView(context: Context) -> UITextField {
        let textField =  UITextField()
        textField.delegate = context.coordinator
        let customView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
        customView.backgroundColor = UIColor.red
        
        textField.inputAccessoryView = customView // <--- Here
        
        return textField
    }
    
    func updateUIView(_ uiView: UITextField, context: Context) {
        
    }
    
    func makeCoordinator() -> TextFieldWithCustomButtons.Coordinator {
        Coordinator(parent: self)
    }
    
    class Coordinator: NSObject, UITextFieldDelegate {
        var parent: TextFieldWithCustomButtons
        
        init(parent: TextFieldWithCustomButtons) {
            self.parent = parent
        }
        
        func textFieldDidChangeSelection(_ textField: UITextField) {
            parent.text = textField.text ?? ""
        }
        
    }
}

Because inputAccessoryView is a UIView and I'm not immediately seeing an easy way to get access to a UIViewController for it, it doesn't seem like there's an obvious solution to embedding SwiftUI in the accessory view. But, if there is a way to embed a UIHostingController in it, that would give you access to SwiftUI for the accessory view, and not just UIKit.

jnpdx
  • 45,847
  • 6
  • 64
  • 94