在基类

时间:2017-09-05 13:22:31

标签: ios swift generics swift3

我正在尝试做什么......

在我的应用程序中,我有很多表单字段看起来很像一堆自定义功能(更改亮点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;属性。

也许我是傻瓜。

1 个答案:

答案 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中常见的模式。