Как сделать так, чтобы многоразовое представление принимало общие типы
Я создал многоразовый элемент управления, который будет использоваться в проекте, над которым я работаю. Это простоUITextField
который показывает UIPickerView
как его inputView
.
class InputPickerView: UIView {
@IBOutlet private var view: UIView!
@IBOutlet weak private var titleLabel: UILabel!
@IBOutlet weak private var textField: UITextField!
private(set) var pickerView = UIPickerView()
var options: [String] = []
var option: String {
get {
return textField.text ?? ""
}
set {
textField.text = newValue
}
}
var title: String = "" {
didSet {
titleLabel.text = title
}
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
Bundle.main.loadNibNamed("InputPickerView", owner: self, options: nil)
addSubview(view)
view.frame = bounds
view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
pickerView.dataSource = self
pickerView.delegate = self
textField.inputView = pickerView
}
}
extension InputPickerView: UITextFieldDelegate {
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
return false
}
}
extension InputPickerView: UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return options.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return options[row]
}
}
extension InputPickerView: UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
textField.text = options[row]
}
}
В настоящее время он принимает только массив строк и возвращает строку. Я пытаюсь сделать его еще более пригодным для повторного использования, заставив его принимать / возвращать любые типы, такие как структуры и перечисления, с помощью дженериков. Я надеялся, что структуры / перечисления будут соответствоватьCustomStringConvertible
и используйте значение свойства description в качестве отображаемого значения для параметров представления средства выбора.
Но мне сложно понять, как это сделать. Все статьи, вопросы и руководства, с которыми я сталкивался, содержат протоколы. Так что я немного запутался.
Как я могу сделать options
а также option
переменные принимают / возвращают любой тип с дженериками?
Под этим я подразумеваю, скажем, я создаю объект с именем State
.
struct State {
let id: Int
let title: String
}
extension State: CustomStringConvertible {
var description: String {
return title
}
}
Вместо того, чтобы передавать в представление строки, я пытаюсь заставить его принимать экземпляры State
объекты в options
свойство и используйте представление description
значение как отображаемое значение. И когда пользователь выбирает один, он возвращает выбранныйState
объект через option
свойство.
1 ответ
Сначала вам нужен протокол, который извлекает строку из ваших соответствующих типов для отображения в средстве выбора:
protocol Presentable {
var title: String { get }
}
Сделайте свой State
структура соответствует Presentable
:
struct State: Presentable {
let id: Int
let title: String
}
Добавьте некоторые общие ограничения к вашему InputPickerView
и всякий раз, когда вам нужен текст из вашей модели, просто ссылайтесь на title
свойство. Обратите внимание: если вы используете дженерики, вы больше не можете создавать расширения для своихUIPickerViewDataSource
а также UIPickerViewDelegate
методы.
class InputPickerView<OptionType: Presentable>: UIView, UIPickerViewDataSource, UIPickerViewDelegate {
private var titleLabel: UILabel!
private var textField: UITextField!
var options: [OptionType] = []
var selectedOption: OptionType?
var title: String = "" {
didSet {
titleLabel.text = title
}
}
// ... Other stuff you need to add ...
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return options.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return options[row].title
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
textField.text = options[row].title
selectedOption = options[row]
}
}
Вы создаете свой InputPickerView
как это:
let pickerView = InputPickerView<State>()
pickerView.options = [
State(id: 1, title: "First"),
State(id: 2, title: "Second"),
State(id: 3, title: "Third"),
]