Как заставить многоразовый просмотр принимать универсальные c типы - PullRequest
1 голос
/ 29 апреля 2020

Я создал многоразовый элемент управления для использования в проекте, над которым я работаю. Это просто UITextField, который показывает UIPickerView в качестве inputView.

enter image description here

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 Ответ

1 голос
/ 30 апреля 2020

Сначала вам нужен протокол, который извлекает строку из ваших соответствующих типов для отображения в средстве выбора:

protocol Presentable {
    var title: String { get }
}

Приведите вашу State структуру в соответствие Presentable:

struct State: Presentable {
    let id: Int
    let title: String
}

Добавьте некоторые общие ограничения c к вашему 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"),
]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...