有没有办法在SwiftUI中为TextField设置inputView?

问题描述 投票:0回答:5

我想将选取器设置为 TextField 的 inputView,我可以仅使用 SwiftUI 来完成它还是必须在框架集成的帮助下使用 UIKit 组件?

UIKit 中的代码示例:

textField.inputView = UIPickerView()

我想做同样的事情,但是使用 SwiftUI 的 TextField

swift swiftui
5个回答
7
投票

使用上述解决方案时我发现的唯一问题是,每当键盘进入编辑阶段时,就会显示选择器,并且键盘也会随之显示。

所以没有办法隐藏键盘并显示选择器。 因此,我编写了一个自定义结构来处理这种行为,类似于我们使用 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)
         }
     }
 }

5
投票

从 Xcode 11.4 开始,SwiftUI 的

TextField
没有与
inputView
UITextField
属性等效的属性。

您可以通过将 UIKit

UITextField
桥接到 SwiftUI,以及将 SwiftUI
Picker
桥接到 UIKit 来解决这个问题。您需要设置文本字段的
inputViewController
属性而不是其
inputView
属性。

UITextField
桥接到 SwiftUI

使用

UIViewRepresentable
UITextField
包装在 SwiftUI
View
中。由于您创建了
UITextField
,因此您可以将其
inputViewController
属性设置为您创建的
UIViewController

将 SwiftUI
Picker
桥接到 UIKit

使用

UIHostingController
将 SwiftUI
Picker
包装在
UIViewController
中。将文本字段的
inputViewController
设置为您的
UIHostingController
实例。


2
投票

这是一个文本字段,可以有一个输入视图,可以是选择器、日期选择器或键盘:

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()
            }
        }
    }
}

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)
    }
  }
}

0
投票

周五:

VStack() {
                    
         Text(self.values[self.selection]).onTapGesture() {
               self.ispickershowing = true
               print("Textfield pressed")
         }
                    
         if self.ispickershowing {
                        
             Picker(selection: $selection, label:
                                Text("Pick one:"))
                  {
                      ForEach(0 ..< values.count) { index in
                             Text(self.values[index])
                                    .tag(index)
                      }
                  }.onChange(of: self.selection){
                        newValore oldValue in
                            print(nuovoValore)
                            print(self.values[self.selection])
                            print(self.values[nuovoValore])
                            self.ispickershowing = false
                        }.pickerStyle(.inline)
                    }
                }
                Spacer()
                Spacer()
                Spacer()
                
                //You can use .onReceive()
       
     }
© www.soinside.com 2019 - 2024. All rights reserved.