我正在尝试做什么......
在我的应用程序中,我有很多表单字段看起来很像一堆自定义功能(更改亮点e.c.t上的颜色)。
我想创建一种包装类,它抽象所有这些代码,然后继承它以实现我的不同输入类型,如日期输入和文本输入。
继承的类只需要为它的类型设置正确的输入控件。
我尝试了什么
这更像是伪代码。我已经尝试了几个小时,但我只是不明白如何实现我需要的东西
我认为我从基类开始,这需要定义对输入控件的引用,以及每个方法将覆盖的一些方法,例如能够设置或获取当前值
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
方法接受的能力正确的数据类型(即String,Int或Date取决于控件的类型)
我相信这可以用泛型来解决,但我真的很难理解如何。如果有人能指出我的方向是正确的那么好。
注意:我不希望或希望任何人为我编写所有代码。伪代码足以让我全力以赴。
尝试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()
}
}
我收到错误:`Protocol'CustomControl'只能用作通用约束,因为它具有Self或相关的类型要求
我最终想要实现的是我输入的一个非常简单的API,我可以设置标签文本,并设置输入值。我的应用程序的其余部分并不关心它是textfield,textview还是完整的自定义输入类型。我的应用程序想要使用协议(或某种类型的基类),它说它有这些方法&amp;属性。
也许我是傻瓜。
答案 0 :(得分:2)
我建议使用协议。
protocol CustomControl {
associatedtype Control
associatedtype Value
var control: Control { get }
func setControlValue(_ value: Value)
}
然后创建符合此protcol的自定义类
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中常见的模式。