In my app I have the need to custom handle text input for which I am using a UIView that conforms to UIKeyInput. This is then wrapped with UIViewRepresentable for use with SwiftUI. I would like to get SwiftUI to add a keyboard menu when my view is selected -- but have been unable to do so.
I have put together a tiny demo app that shows the difference between how a standard TextField and my custom view are treated. (Using custom text view from hackingwithswift.com as a standin for my actual use case.) The app includes a TextField - when it is active, a system toolbar is added as well as a custom toolbar. Though, the toolbar shows two buttons - one that I intended for the TextField, and also the one I intended for my custom view. When my custom view is activated, no toolbars are added. Mainly, I am after getting the custom toolbar (with blue buttons) added.
How do I get my view to be treated similar to a TextField?
ContentView:
import SwiftUI
enum LocFields {
case textField
case myTextView
}
struct ContentView: View {
@State private var tfText = ""
@State private var isEditingTvText = false
@FocusState private var focusedField: LocFields?
var body: some View {
VStack {
TextField("Enter text", text: $tfText)
.textFieldStyle(.roundedBorder)
.focused($focusedField, equals: .textField)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Tap me 1!") {
print("Tapped 1")
}
}
}
TextView(isEditing: isEditingTvText)
.background(Color.gray.opacity(0.2))
.focused($focusedField, equals: .myTextView)
.toolbar {
ToolbarItemGroup(placement: .keyboard) {
Button("Tap me 2!") {
print("Tapped 2")
}
}
}
.onTapGesture {
isEditingTvText.toggle()
}
}
.padding()
.onChange(of: focusedField) { _, newValue in
switch newValue {
case .textField:
isEditingTvText = false
case .myTextView:
break
default: // Set to nil case
isEditingTvText = false
}
}
}
}
TextView:
import SwiftUI
struct TextView: UIViewRepresentable {
var isEditing: Bool
// NOTE: TextRenderingView from:
// https://www.hackingwithswift.com/example-code/uikit/how-to-create-custom-text-input-using-uikeyinput
func makeUIView(context: Context) -> TextRenderingView {
let vw = TextRenderingView()
vw.backgroundColor = .clear
return vw
}
func updateUIView(_ uiView: TextRenderingView, context: Context) {
let uiViewIsFirst = uiView.isFirstResponder
if isEditing {
if !uiViewIsFirst {
DispatchQueue.main.async {
uiView.becomeFirstResponder()
}
}
} else {
if uiViewIsFirst {
DispatchQueue.main.async {
uiView.resignFirstResponder()
}
}
}
}
}
TextRenderingView:
import UIKit
// From: https://www.hackingwithswift.com/example-code/uikit/how-to-create-custom-text-input-using-uikeyinput
class TextRenderingView: UIView, UIKeyInput {
// the string we'll be drawing
var input = ""
override var canBecomeFirstResponder: Bool {
true
}
var hasText: Bool {
input.isEmpty == false
}
func insertText(_ text: String) {
input += text
setNeedsDisplay()
}
func deleteBackward() {
_ = input.popLast()
setNeedsDisplay()
}
override func draw(_ rect: CGRect) {
let attrs: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 32)]
let attributedString = NSAttributedString(string: input, attributes: attrs)
attributedString.draw(in: rect)
}
}

toolbardoes not work on it. Just setinputAccessoryViewto whatever you want.