[英]How to make a reusable view accept generic types
I created a reusable control to be used in a project I'm working on.我创建了一个可重用的控件,用于我正在处理的项目中。 It's just a
UITextField
which shows a UIPickerView
as its inputView
.它只是一个
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]
}
}
Currently it only accepts an array of strings and returns a string.目前它只接受一个字符串数组并返回一个字符串。 I'm trying to make it even more reusable by make it accept/return any types such as structs and enums with the help of generics.
我试图通过在 generics 的帮助下接受/返回任何类型(例如结构和枚举)来使其更具可重用性。 I was hoping to make the structs/enums conform to
CustomStringConvertible
and use the description property value as the display value for the picker view options.我希望使结构/枚举符合
CustomStringConvertible
并使用描述属性值作为选择器视图选项的显示值。
But I'm having trouble figuring out how to do that.但我很难弄清楚如何做到这一点。 All the articles, questions, tutorials I came across have protocols involved in them.
我遇到的所有文章、问题、教程都涉及协议。 So I'm a little confused.
所以我有点困惑。
How can I make the options
and option
variables accept/return any type with generics?如何使用 generics 使
options
和option
变量接受/返回任何类型?
By which I mean, say I create an object called State
.我的意思是说,我创建了一个名为 State 的
State
。
struct State {
let id: Int
let title: String
}
extension State: CustomStringConvertible {
var description: String {
return title
}
}
Instead of passing in strings to the view, I'm trying to make it accept instances of State
objects in the options
property and have the view use the description
value as the display value.我没有将字符串传递给视图,而是试图让它在
options
属性中接受State
对象的实例,并让视图使用description
值作为显示值。 And when the user selects one, it returns the selected State
object via the option
property.当用户选择一个时,它通过
option
属性返回选择的State
object。
First you need a protocol that extracts a string from your conforming types to display in the picker:首先,您需要一个协议,从您的符合类型中提取字符串以显示在选择器中:
protocol Presentable {
var title: String { get }
}
Make your State
struct conform to Presentable
:使您的
State
结构符合Presentable
:
struct State: Presentable {
let id: Int
let title: String
}
Add some generic constraints to your InputPickerView
and whenever you need the text from your model just reference the title
property.向 InputPickerView 添加一些通用约束,当您需要
InputPickerView
中的文本时,只需引用title
属性即可。 Note that if you use generics you can no longer create extensions for your UIPickerViewDataSource
and UIPickerViewDelegate
methods.请注意,如果您使用 generics 您不能再为您的
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]
}
}
You create your InputPickerView
like this:您可以像这样创建
InputPickerView
:
let pickerView = InputPickerView<State>()
pickerView.options = [
State(id: 1, title: "First"),
State(id: 2, title: "Second"),
State(id: 3, title: "Third"),
]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.