Swiftui Inputaccesoryview Implementation

InputAccessoryView / View Pinned to Keyboard with SwiftUI

I got something working which is quite near the wanted result. So at first, it's not possible to do this with SwiftUI only. You still have to use UIKit for creating the UITextField with the wanted "inputAccessoryView". The textfield in SwiftUI doesn't have the certain method.

First I created a new struct:

import UIKit
import SwiftUI

struct InputAccessory: UIViewRepresentable {

func makeUIView(context: Context) -> UITextField {

let customView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
customView.backgroundColor = UIColor.red
let sampleTextField = UITextField(frame: CGRect(x: 20, y: 100, width: 300, height: 40))
sampleTextField.inputAccessoryView = customView
sampleTextField.placeholder = "placeholder"

return sampleTextField
}
func updateUIView(_ uiView: UITextField, context: Context) {
}
}

With that I could finally create a new textfield in the body of my view:

import SwiftUI

struct Test: View {
@State private var showInput: Bool = false
var body: some View {
HStack{
Spacer()
if showInput{
InputAccessory()
}else{
InputAccessory().hidden()
}
}
}
}

Now you can hide and show the textfield with the "showInput" state. The next problem is, that you have to open your keyboard at a certain event and show the textfield. That's again not possible with SwiftUI and you have to go back to UiKit and making it first responder. If you try my code, you should see a red background above the keyboard. Now you only have to move the field up and you got a working version.

Overall, at the current state it's not possible to work with the keyboard or with the certain textfield method.

SwiftUI inputAccesoryView Implementation

Here is a demo with custom toolbar & binding for entered text, but simplified by excluding on dismiss callback (as it is not important for approach demo), just to have less code. Hope it will be helpful.

import SwiftUI
import UIKit
import Combine

struct CustomInputTextField : UIViewRepresentable {

@Binding var text: String

let textField = UITextField(frame: CGRect(x:0, y:0, width: 100, height: 32)) // just any

func makeUIView(context: UIViewRepresentableContext<CustomInputTextField>) -> UITextField {
return textField
}

func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<CustomInputTextField>) {
self.textField.text = text
}

func makeCoordinator() -> CustomInputTextField.Coordinator {
let coordinator = Coordinator(self)

// configure a toolbar with a Done button
let toolbar = UIToolbar()
toolbar.setItems([
// just moves the Done item to the right
UIBarButtonItem(
barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace
, target: nil
, action: nil
)
, UIBarButtonItem(
title: "Done"
, style: UIBarButtonItem.Style.done
, target: coordinator
, action: #selector(coordinator.onSet)
)
]
, animated: true
)
toolbar.barStyle = UIBarStyle.default
toolbar.sizeToFit()

textField.inputAccessoryView = toolbar
return coordinator
}

typealias UIViewType = UITextField

class Coordinator: NSObject {
let owner: CustomInputTextField
private var subscriber: AnyCancellable

init(_ owner: CustomInputTextField) {
self.owner = owner
subscriber = NotificationCenter.default.publisher(for: UITextField.textDidChangeNotification, object: owner.textField)
.sink(receiveValue: { _ in
owner.$text.wrappedValue = owner.textField.text ?? ""
})
}

@objc fileprivate func onSet() {
owner.textField.resignFirstResponder()
}

}
}

struct DemoCustomKeyboardInput : View {

@State var email:String = ""

var body: some View {
VStack{
CustomInputTextField(text: $email).border(Color.black)
.padding(.horizontal)
.frame(maxHeight: 32)
Divider()
Text("Entered text: \(email)")
}
}
}

struct DemoCustomKeyboardInput_Previews: PreviewProvider {
static var previews: some View {
DemoCustomKeyboardInput()
}
}

Add a view on top of the keyboard using InputAccessoryView

To get a view to stick above the keyboard, the code itself is pretty simple. The code you posted is not correct, try this (note that you must connect textField to the UITextField in your storyboard):

@IBOutlet weak var textField: UITextField!

override func viewDidLoad() {
super.viewDidLoad()

let customView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 44))
customView.backgroundColor = UIColor.red
textField.inputAccessoryView = customView
}

Is there any way to set inputView for TextField in SwiftUI?

As of Xcode 11.4, SwiftUI's TextField does not have an equivalent of the inputView property of UITextField.

You can work around it by bridging a UIKit UITextField to SwiftUI, and by bridging a SwiftUI Picker to UIKit. You'll need to set the text field's inputViewController property rather than its inputView property.

To bridge a UITextField to SwiftUI

Use UIViewRepresentable to wrap the UITextField in a SwiftUI View. Since you create the UITextField, you can set its inputViewController property to a UIViewController that you create.

To bridge a SwiftUI Picker into UIKit

UseUIHostingController to wrap a SwiftUI Picker in a UIViewController. Set the text field's inputViewController to your UIHostingController instance.

Extend SwiftUI Keyboard with Custom Button

Solved the issue using UIRepresentable protocol

struct TestTextfield: UIViewRepresentable {
@Binding var text: String
var keyType: UIKeyboardType
func makeUIView(context: Context) -> UITextField {
let textfield = UITextField()
textfield.keyboardType = keyType
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textfield.frame.size.width, height: 44))
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(textfield.doneButtonTapped(button:)))
toolBar.items = [doneButton]
toolBar.setItems([doneButton], animated: true)
textfield.inputAccessoryView = toolBar
return textfield
}

func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = text

}
}

extension UITextField{
@objc func doneButtonTapped(button:UIBarButtonItem) -> Void {
self.resignFirstResponder()
}

}

Using in content view

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

var body: some View {
TestTextfield(text: $text, keyType: UIKeyboardType.phonePad)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 50)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.blue, lineWidth: 4)
)
}
}


Related Topics



Leave a reply



Submit