【问题标题】:How to make a reusable view accept generic types如何使可重用视图接受泛型类型
【发布时间】:2020-04-29 15:32:49
【问题描述】:

我创建了一个可重用的控件,用于我正在处理的项目中。它只是一个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 并使用描述属性值作为选择器视图选项的显示值。

但我无法弄清楚如何做到这一点。我遇到的所有文章、问题、教程都涉及协议。所以我有点困惑。

如何使optionsoption 变量接受/返回任何具有泛型的类型?

我的意思是说,我创建了一个名为 State 的对象。

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

extension State: CustomStringConvertible {
    var description: String {
        return title
    }
}

我试图让它接受options 属性中State 对象的实例,并让视图使用description 值作为显示值,而不是将字符串传递给视图。而当用户选择一个时,它通过option属性返回选择的State对象。

Demo project

【问题讨论】:

  • 可以通过协议完成
  • @jawadAli 你能详细说明一下吗?或者给我举个例子?
  • “返回任何类型”是什么意思?
  • @Rob 我用一些解释更新了我的问题:)
  • @Isuru 这是可行的,但你必须使用泛型。如果你使用泛型,你不能为你的InputPickerView 使用 xib。您对以编程方式布置视图有何感想?

标签: ios swift generics uiview customstringconvertible


【解决方案1】:

首先,您需要一个协议,从您的符合类型中提取字符串以显示在选择器中:

protocol Presentable {
    var title: String { get }
}

使您的State 结构符合Presentable

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

为您的InputPickerView 添加一些通用约束,当您需要模型中的文本时,只需引用title 属性即可。请注意,如果您使用泛型,则无法再为您的 UIPickerViewDataSourceUIPickerViewDelegate 方法创建扩展。

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"),
]

【讨论】:

  • 谢谢。这正是我想要的。
猜你喜欢
  • 2020-05-20
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-21
相关资源
最近更新 更多