我想将选择器设置为TextField的inputView,可以仅使用SwiftUI进行操作,还是必须在框架集成的帮助下使用UIKit组件?
UIKit中的代码示例:
textField.inputView = UIPickerView()
我想做同样的事情,但是要用SwiftUI的TextField
答案 0 :(得分:2)
我使用上述解决方案发现的唯一问题是,每当键盘进入编辑阶段时,便会显示选择器,同时也会显示选择器。
因此无法隐藏键盘并显示选择器。 因此,我编写了一个自定义结构来处理此行为,类似于使用UITextField inputView进行的操作。 您可以使用它。这适用于我的用例。
您还可以自定义选择器,以及像我一样的makeUIView方法中的文本字段,以完成选择器的背景色。
struct TextFieldWithPickerAsInputView : UIViewRepresentable {
var data : [String]
var placeholder : String
@Binding var selectionIndex : Int
@Binding var text : String?
private let textField = UITextField()
private let picker = UIPickerView()
func makeCoordinator() -> TextFieldWithPickerAsInputView.Coordinator {
Coordinator(textfield: self)
}
func makeUIView(context: UIViewRepresentableContext<TextFieldWithPickerAsInputView>) -> UITextField {
picker.delegate = context.coordinator
picker.dataSource = context.coordinator
picker.backgroundColor = .yellow
picker.tintColor = .black
textField.placeholder = placeholder
textField.inputView = picker
textField.delegate = context.coordinator
return textField
}
func updateUIView(_ uiView: UITextField, context: UIViewRepresentableContext<TextFieldWithPickerAsInputView>) {
uiView.text = text
}
class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate , UITextFieldDelegate {
private let parent : TextFieldWithPickerAsInputView
init(textfield : TextFieldWithPickerAsInputView) {
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.text = self.parent.data[self.parent.selectionIndex]
self.parent.textField.endEditing(true)
}
func textFieldDidEndEditing(_ textField: UITextField) {
self.parent.textField.resignFirstResponder()
}
}
}
您可以将其用作:-
struct ContentView : View {
@State var gender : String? = nil
@State var arrGenders = ["Male","Female","Unknown"]
@State var selectionIndex = 0
var body : some View {
VStack {
TextFieldWithPickerAsInputView(data: self.arrGenders, placeholder: "select your gender", selectionIndex: self.$selectionIndex, text: self.$gender)
}
}
}
答案 1 :(得分:1)
从Xcode 11 GM Seed 2开始,SwiftUI的TextField
不具有inputView
的{{1}}属性。
您可以通过将UIKit UITextField
桥接到SwiftUI,以及将SwiftUI UITextField
桥接到UIKit来解决此问题。您需要设置文本字段的Picker
属性,而不是其inputViewController
属性。
inputView
桥接到SwiftUI 使用UITextField
将UIViewRepresentable
包装在SwiftUI UITextField
中。自创建View
以来,您可以将其UITextField
属性设置为您创建的inputViewController
。
UIViewController
桥接到UIKit 使用Picker
将SwiftUI UIHostingController
包装在Picker
中。将文本字段的UIViewController
设置为您的inputViewController
实例。
答案 2 :(得分:1)
如果您想拥有TextField
,并使用Picker
中的SwiftUI
选择其文本。而且您不想将UIKit
集成到SwiftUI
中,下面的解决方案可能会给您一些想法:
import SwiftUI
struct ContentView: View {
@State private var selection = 0
@State private var textfieldValue = ""
@State private var textfieldValue2 = ""
@State private var ispickershowing = false
var values = ["V1", "V2", "V3"]
var body: some View {
VStack {
TextField("Pick one from the picker:", text: $textfieldValue, onEditingChanged: {
edit in
if edit {
self.ispickershowing = true
} else {
self.ispickershowing = false
}
})
if ispickershowing {
Picker(selection: $selection, label:
Text("Pick one:")
, content: {
ForEach(0 ..< values.count) { index in
Text(self.values[index])
.tag(index)
}
})
Text("you have picked \(self.values[self.selection])")
Button(action: {
self.textfieldValue = self.values[self.selection]
}, label: {
Text("Done")
})
}
TextField("simple textField", text: $textfieldValue2)
}
}
}
答案 3 :(得分:0)
这是一个文本字段,可以有一个输入视图,它可以是选择器、日期选择器或键盘:
import Foundation
import SwiftUI
struct CTextField: UIViewRepresentable {
enum PickerType {
case keyboard(type: UIKeyboardType, autocapitalization: UITextAutocapitalizationType, autocorrection: UITextAutocorrectionType)
case datePicker(minDate: Date, maxDate: Date)
case customList(list: [String])
}
var pickerType: CTextField.PickerType
@Binding var text: String {
didSet{
print("text aha: ", text)
}
}
let placeholder: String
func makeUIView(context: Context) -> UITextField {
let textField = UITextField()
textField.delegate = context.coordinator
textField.placeholder = placeholder
textField.frame.size.height = 36
textField.borderStyle = .roundedRect
textField.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
if !self.text.isEmpty{
textField.text = self.text
}
return textField
}
func updateUIView(_ uiView: UITextField, context: Context) {
switch pickerType {
case .datePicker:
uiView.text = self.text
case .customList:
uiView.text = self.text
default:
break
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
final class Coordinator: NSObject {
var parent: CTextField
init(_ parent: CTextField) {
self.parent = parent
}
private func setPickerType(textField: UITextField) {
switch parent.pickerType {
case .keyboard(let type, let autocapitalization, let autocorrection):
textField.keyboardType = type
textField.inputView = nil
textField.autocapitalizationType = autocapitalization
textField.autocorrectionType = autocorrection
case .customList(let list):
textField.inputView = getPicker()
let row = list.firstIndex(of: parent.text)
let myPicker = textField.inputView as! UIPickerView
myPicker.selectRow(row!, inComponent: 0, animated: true)
case .datePicker(let minDate, let maxDate):
textField.inputView = getDatePicker(minDate: minDate, maxDate: maxDate)
}
textField.inputAccessoryView = getToolBar()
}
private func getPicker() -> UIPickerView {
let picker = UIPickerView()
picker.backgroundColor = UIColor.systemBackground
picker.delegate = self
picker.dataSource = self
return picker
}
private func getDatePicker(minDate: Date, maxDate: Date) -> UIDatePicker {
let picker = UIDatePicker()
picker.datePickerMode = .date
picker.backgroundColor = UIColor.systemBackground
picker.maximumDate = maxDate
picker.minimumDate = minDate
picker.addTarget(self, action: #selector(handleDatePicker(sender:)), for: .valueChanged)
return picker
}
@objc func handleDatePicker(sender: UIDatePicker) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd MMM yyyy"
parent.text = dateFormatter.string(from: sender.date)
}
private func getToolBar() -> UIToolbar {
let toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.default
toolBar.backgroundColor = UIColor.systemBackground
toolBar.isTranslucent = true
toolBar.sizeToFit()
let spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItem.Style.done, target: self, action: #selector(self.donePicker))
toolBar.setItems([spaceButton, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
return toolBar
}
@objc func donePicker() {
UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
}
}
}
extension CTextField.Coordinator: UIPickerViewDataSource{
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
switch parent.pickerType {
case .customList(let list):
return list.count
default:
return 0
}
}
}
extension CTextField.Coordinator: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
switch parent.pickerType {
case .customList(let list):
return list[row]
default:
return ""
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
switch parent.pickerType {
case .customList(let list):
parent.text = list[row]
print("parent.text is now: ", parent.text)
default:
break
}
}
}
extension CTextField.Coordinator: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
setPickerType(textField: textField)
return true
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
defer {
if let currentText = textField.text, let stringRange = Range(range, in: currentText) {
parent.text = currentText.replacingCharacters(in: stringRange, with: string)
}
}
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
donePicker()
}
}
假设你有一个这样的 ViewModel:
import SwiftUI
import Combine
let FRIENDS = ["Raquel", "Shekinah", "Sedh", "Sophia"]
class UserSettingsVM: ObservableObject {
@Published var name = FRIENDS[0]
@Published var greet = ""
}
你可以这样使用它:
import SwiftUI
struct FriendsView: View {
@ObservedObject var vm = UserSettingsVM()
var body: some View {
ScrollView {
VStack {
Group {
Text(vm.name)
.padding()
Text(vm.greet)
.padding()
CTextField(pickerType: .customList(list: FRIENDS), text: $vm.name, placeholder: "Required")
CTextField(pickerType: .keyboard(type: .default, autocapitalization: .none, autocorrection: .no
), text: $vm.greet, placeholder: "Required")
}
.padding()
}
}
}
}