繁体   English   中英

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

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

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

UIKit 中的代码示例:

textField.inputView = UIPickerView()

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

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

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

从 Xcode 11.4 开始,SwiftUI 的TextField没有UITextFieldinputView属性。

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

UITextField桥接到 SwiftUI

使用UIViewRepresentableUITextField包装在 SwiftUI View 由于您创建了UITextField ,您可以将其inputViewController属性设置为您创建的UIViewController

将 SwiftUI Picker桥接到 UIKit

使用UIHostingController将 SwiftUI Picker包装在UIViewController 将文本字段的inputViewController为您的UIHostingController实例。

如果您想拥有一个TextField并使用SwiftUIPicker选择其文本。 而且你不想在SwiftUI集成UIKit ,下面的解决方案可能会给你一些想法:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM