如何使可重用视图接受泛型类型
How to make a reusable view accept generic types
我创建了一个可重复使用的控件,用于我正在处理的项目。它只是一个 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]
}
}
目前它只接受一个字符串数组和returns一个字符串。我试图通过在泛型的帮助下使它 accept/return 任何类型(例如结构和枚举)来使其更具可重用性。我希望 structs/enums 符合 CustomStringConvertible
并使用描述 属性 值作为选择器视图选项的显示值。
但我不知道该怎么做。我遇到的所有文章、问题、教程都涉及到协议。所以我有点困惑。
如何使用泛型将 options
和 option
变量 accept/return 设为任意类型?
我的意思是,假设我创建了一个名为 State
的对象。
struct State {
let id: Int
let title: String
}
extension State: CustomStringConvertible {
var description: String {
return title
}
}
我没有将字符串传递给视图,而是试图让它接受 options
属性 中 State
对象的实例,并让视图使用 description
值作为显示值。当用户选择一个时,它 returns 通过 option
属性.
选择的 State
对象
首先,您需要一个协议,从您的符合类型中提取字符串以显示在选择器中:
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"),
]
我创建了一个可重复使用的控件,用于我正在处理的项目。它只是一个 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]
}
}
目前它只接受一个字符串数组和returns一个字符串。我试图通过在泛型的帮助下使它 accept/return 任何类型(例如结构和枚举)来使其更具可重用性。我希望 structs/enums 符合 CustomStringConvertible
并使用描述 属性 值作为选择器视图选项的显示值。
但我不知道该怎么做。我遇到的所有文章、问题、教程都涉及到协议。所以我有点困惑。
如何使用泛型将 options
和 option
变量 accept/return 设为任意类型?
我的意思是,假设我创建了一个名为 State
的对象。
struct State {
let id: Int
let title: String
}
extension State: CustomStringConvertible {
var description: String {
return title
}
}
我没有将字符串传递给视图,而是试图让它接受 options
属性 中 State
对象的实例,并让视图使用 description
值作为显示值。当用户选择一个时,它 returns 通过 option
属性.
State
对象
首先,您需要一个协议,从您的符合类型中提取字符串以显示在选择器中:
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"),
]