How to Set Inputview for Textfield in Swiftui

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.

UIPickerView as input for TextField() in SwiftUI

SwiftUI's TextField does not have inputView property like as UITextField.

But You can use UITextfield and UIPickerView using UIViewRepresentable

First make TextFieldWithInputView.swift file and add below code in it

struct TextFieldWithInputView : UIViewRepresentable {

var data : [String]
var placeholder : String

@Binding var selectionIndex : Int
@Binding var selectedText : String?

private let textField = UITextField()
private let picker = UIPickerView()

func makeCoordinator() -> TextFieldWithInputView.Coordinator {
Coordinator(textfield: self)
}

func makeUIView(context: UIViewRepresentableContext<TextFieldWithInputView>) -> UITextField {
picker.delegate = context.coordinator
picker.dataSource = context.coordinator
picker.backgroundColor = .gray
picker.tintColor = .black
textField.placeholder = placeholder
textField.inputView = picker
textField.delegate = context.coordinator
return textField
}

func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<TextFieldWithInputView>) {
uiView.text = selectedText
}

class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate , UITextFieldDelegate {

private let parent : TextFieldWithInputView

init(textfield : TextFieldWithInputView) {
self.parent = textfield
}

func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return self.parent.data.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return self.parent.data[row]
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
self.parent.$selectionIndex.wrappedValue = row
self.parent.selectedText = self.parent.data[self.parent.selectionIndex]
self.parent.textField.endEditing(true)

}
func textFieldDidEndEditing(_ textField: UITextField) {
self.parent.textField.resignFirstResponder()
}
}
}

Then, You can use it in your contentView like as below

struct ContentView : View {

@State var country : String? = nil
@State var arrCountry = ["India","USA","France"] //Here Add Your data
@State var selectionIndex = 0

var body : some View {
VStack {
TextFieldWithInputView(data: self.arrCountry, placeholder: "Select your country", selectionIndex: self.$selectionIndex, selectedText: self.$country)
.frame(width: 300, height: 50)
.border(Color.black)

}
}
}

Here is output

Sample Image

How to set Keyboard type of TextField in SwiftUI?

To create a field where you can enter secure text you can use SecureField($password)

https://developer.apple.com/documentation/swiftui/securefield

If you want to set the contentType of an textField to eg. .oneTimeCode you can do it like this. Also works with keyboardType

TextField($code)
.textContentType(.oneTimeCode)
.keyboardType(.numberPad)

SwiftUI custom textfield not getting updated value

The binding value will not update if you don't implement theUITextFieldDelegate method(s) to receive user input (SwiftUI Custom TextField with UIViewRepresentable Issue with ObservableObject and pushed View)

When you change the value from the SwiftUI side, your UIViewRepresentable needs to handle the change in updateUIView (pass the new value to UITextField)

Also remember to set textfield.delegate = context.coordinator so that you receive events.

struct TextInput: UIViewRepresentable {
@Binding var value: Double

func makeCoordinator() -> Coordinator { Coordinator(self) }

// for handling swiftui-side modification to the binding (your buttons)
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = value
}

func makeUIView(context: Context) -> UITextField {
let textfield = UITextField()
textfield.placeholder = "Enter amount"
textfield.text = "\(value)"
textfield.keyboardType = .decimalPad
textfield.setContentHuggingPriority(.defaultHigh, for: .vertical)
textfield.setContentHuggingPriority(.defaultLow, for: .horizontal)
textfield.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textfield.delegate = context.coordinator // set the delegate
return textfield
}

class Coordinator: NSObject, UITextFieldDelegate {
var parent: TextInput
init(_ textField: TextInput) { self.parent = textField }
func textFieldDidEndEditing(_ textField: UITextField) {
if let text = textField.text {
parent.value = Double(text) ?? 0
} else {
parent.value = 0
}
}
}
}

How to set up a textfield with Picker as input in SwiftUI

As far as I understood your code the issue is due to not aligned types for selection (count is-a String) and items (item is-a Int) in Picker, so here is a fix (use tag with same type as selection):

Picker(selection: $count,label: EmptyView()) {
ForEach(1..<100){ number in
Text("\(number)").tag("\(number)") // << here !!
}
}

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.

How do I validate dynamically added textFields on a button click in SwiftUI?

You can be done this by making a model of text fields and use one isValid flag for each InputView for the track.

Here, is the possible demo solution.

struct TextFieldModel: Identifiable {
var id = UUID()
var input: String
var correctInput: Int
var isValidate: Bool = true
}

struct InputView: View {
@Binding var input: TextFieldModel
var body: some View {
TextField("?", text: $input.input)
.foregroundColor(input.isValidate ? Color.blue : Color.red)
}
}

struct ContentViewTextFields: View {
@State var arrTextFields: [TextFieldModel] = [
.init(input: "", correctInput: 5),
.init(input: "", correctInput: 10),
.init(input: "", correctInput: 1)
]

@State var isValidate: Bool = true

var body: some View {
VStack{
ForEach(arrTextFields.indices) { index in
InputView(input: $arrTextFields[index])
.background(Color.gray.opacity(0.2))
.padding()
}
Spacer()

Button("Validate") {
// Here validate all text
arrTextFields.indices.forEach({arrTextFields[$0].isValidate = (Int(arrTextFields[$0].input) == arrTextFields[$0].correctInput) })
}
}
}
}

Sample Image



Related Topics



Leave a reply



Submit