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
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) })
}
}
}
}
Related Topics
Swift - Initialize View Controller from Storyboard by Overriding Init
Swift, Nsjsonserialization and Nserror
Xcode 8 Swift Update with Error "Use Legacy Swift Language Version"
Understanding @Binding in Swiftui
Facebookshare Causing Compiler Error After Update
Swift Calling Static Methods: Type(Of: Self) VS Explicit Class Name
Swift Check Type Against a Generic Type
Scrollbar Incorrectly Appears Underneath Uicollectionview Section Header
Swift Struct Doesn't Conform to Protocol Equatable
Convert Array of Unicodescalar into String in Swift
Can You Use String/Character Literals Within Swift String Interpolation
Making Nsdecimalnumber Codable
How to Cancel Alamofire.Upload
Swift Combine Alternative to Rx Observable.Create
Why Does Filters in Swift Iterate the Collection Twice