在基类中使用泛型函数
我试图做...在基类中使用泛型函数
在我的应用我有很多看起来很像带着一帮自定义功能(在突出改变颜色的表单字段ECT )。
我想创建一个包装类,它抽象所有这些代码,然后从中继承来实现我不同的输入类型,如日期输入和文本输入。
继承的类只需要为它的类型设置正确的输入控件。
我已经试过
这更像是伪代码。我一直在尝试几个小时,但我只是不明白如何实现我所需要的
我想我从一个基类开始,这需要定义一个对输入控件的引用,以及每个方法的一些方法将覆盖,如能够设置或获得当前值
class BaseInput<T>: UIView {
let label = UILabel()
let control: T
... A bunch of methods for layout and stuff ...
func setControlValue(_ value: U) {
print("I'm not a real input yet, so i can't do that")
}
}
然后,我创建一个日期输入的继承类。它使用一个基本的标签控制,并在内部将使用的UIDatePicker设定值
class DateInput: BaseInput<UILabel> {
override init() {
self.control = UILabel()
}
override func setControlValue(_ value: Date) {
MyGlobalDateFormatter.string(format:value)
}
}
,另一个用于文本输入字段
class TextInput: BaseInput<UITextField> {
override init() {
self.control = UITextField()
}
override func setControlValue(_ value: String) {
control.textLabel!.text = value
}
}
什么,我最终要的是能力初始化一个输入组件,并且MyInput.control
属性为该特定输入的正确类,并且setControlValue
方法接受正确类型的数据(即字符串,整数或日期,具体取决于控件的类型)
我相信这可以使用泛型的解决,但我真的很难理解如何。如果任何人都能指出我会朝着正确的方向发展,那将是很棒的。
注意:我不期望或不希望任何人为我编写所有代码。伪代码足以让我全力以赴。
尝试1:
protocol CustomControl {
associatedtype Control
associatedtype Value
var control : Control { get }
var label : UILabel { get }
func setControlValue(_ value: Value)
}
class AbstractInputField: UIView {
// This class contains all the setup
// for the label and wrapping UI View
}
class TextInputField: AbstractInputField, CustomControl {
typealias Control = UITextField
typealias Value = String
let control = UITextField()
func setControlValue(_ value: String) {
control.text = value
}
}
class DateInputField: AbstractInputField, CustomControl {
typealias Control = UILabel
typealias Value = String
let control = UILabel()
private let picker = UIDatePicker()
func setControlValue(_ value: Date) {
control.text = GlobalDateFormatter.string(from: value)
}
.. Also in this class it's a bunch of date picker methods ..
}
在其他地方,如果我做的:
override func viewDidLoad() {
let firstInput = makeControl("text")
firstInput.label.text = "First name"
firstInput.setControlValue(myUser.first_name)
let dobInput = makeControl("date")
dobInput.label.text = "Date of birth"
dobInput.setControlValue(myUser.dob)
}
func makeControl(controlType: String) -> CustomControl {
// Im using strings just for testing, i'd probably make this an enum or something
if controlType == "text" {
return TextInputField()
} else {
return DateInputField()
}
}
我得到的错误:'协议 'CustomControl' 只能作为一种通用的约束,因为它有自我或相关的类型要求
我最终试图实现的是一个非常简单的API来输入我可以设置标签文本,并设置输入值。我的应用程序的其余部分不关心它是文本字段,textview还是完整的自定义输入类型。我的应用程序想要使用协议(或某种基类)来说明它具有这些方法的属性。
也许我很愚蠢。
我会建议使用协议。
protocol CustomControl {
associatedtype Control
associatedtype Value
var control: Control { get }
func setControlValue(_ value: Value)
}
,然后创建自定义类符合这个协议型
class CustomTextField: CustomControl {
typealias Control = UITextField
typealias Value = String
let control: UITextField
init() {
self.control = UITextField()
}
func setControlValue(_ value: String) {
control.text = value
}
}
编辑:
class CustomLabel: CustomControl {
typealias Control = UILabel
typealias Value = String
let control: UILabel
init() {
self.control = UILabel()
}
func setControlValue(_ value: String) {
control.text = value
}
}
编辑2:替代做法
protocol HasSettableValue: class {
associatedtype Value
var customValue: Value { get set }
}
protocol IsInitializable {
init()
}
extension UITextField: IsInitializable {}
extension UITextField: HasSettableValue {
typealias Value = String?
var customValue: String? {
get {
return text
}
set {
text = newValue
}
}
}
class BaseClass<T> where T: HasSettableValue, T: IsInitializable {
let control: T
init() {
self.control = T()
}
func setControlValue(_ value: T.Value) {
control.customValue = value
}
}
class CustomTextField: BaseClass<UITextField> {
}
let customTextField = CustomTextField()
customTextField.setControlValue("foo")
print(customTextField.control) // prints <UITextField: 0x...; frame = (0 0; 0 0); text = 'foo'; opaque = NO; layer = <CALayer: 0x...>>
最终更新:
协议具有关联类型的问题是你不能用它们来声明变量。你总是需要指定具体的实现。
我想我找到了一个解决方案适合您的需要:
enum ControlType {
case textField, datePicker
}
enum ControlValueType {
case text(text: String)
case date(date: Date)
var string: String {
switch self {
case .text(text: let text):
return text
case .date(date: let date):
// apply custom format
return "\(date)"
}
}
var date: Date {
switch self {
case .date(date: let date):
return date
default:
preconditionFailure("`date` can be only used with `.date` value type")
}
}
}
protocol Control: class {
var controlValue: ControlValueType { get set }
}
class TextInputField: Control {
private let textField = UITextField()
var controlValue: ControlValueType {
get {
return .text(text: textField.text ?? "")
}
set {
textField.text = newValue.string
}
}
}
class DateInputField: Control {
private let picker = UIDatePicker()
var controlValue: ControlValueType {
get {
return .date(date: picker.date)
}
set {
picker.date = newValue.date
}
}
}
func createControl(ofType type: ControlType) -> Control {
switch type {
case .textField:
return TextInputField()
case .datePicker:
return DateInputField()
}
}
let customDatePicker = createControl(ofType: .datePicker)
customDatePicker.controlValue = .date(date: Date())
print(customDatePicker.controlValue.string) // prints 2017-09-06 10:47:22 +0000
let customTextFiled = createControl(ofType: .textField)
customTextFiled.controlValue = .text(text: "Awesome text")
print(customTextFiled.controlValue.string) // prints Awesome text
我希望这有助于。 PS:你试图实现的目标不是iOS中很常见的模式,我知道。
有趣。谢谢你,我会快速玩这个:) – TRG
在这种情况下你会如何处理BaseClass?理想情况下,我可以在同一个实例上使用'input.control'和'input.label'来分别获取标签或控件。 我应该在'BaseInputField'上实现'CustomControl',并让控件和值类型像'UILabel'&'String'一样泛型并在其他类中重写? 我想另一种选择是根据我想访问的内容将我的实例投射到'BaseInputField'或'CustomControl',但这看起来不是正确的选项。 – TRG
我真的不明白你想达到什么目的。当日期输入和文本输入字段可以有标签或文本字段时,我会创建两个符合'CustomControl'协议的不同类。 –