首页 > 解决方案 > UILabel 继承的 Swift 自定义 UIView

问题描述

我有一个正在尝试解决的继承问题。通常,我在这里只使用多重继承,但 Swift 并没有真正做到这一点。

自定义 UIView

import UIKit

class ValidationView: UIView {

    var required:Bool = false
    var validRegex:String? = nil

    var requiredLbl:UILabel?

    private var requiredColor:UIColor = UIColor.red
    private var requiredText:String = "*"
    private var requiredFont:UIFont = UIFont.systemFont(ofSize: 16.0, weight: UIFont.Weight.bold)

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setupValidationViews()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupValidationViews()
    }

    private func setupValidationViews() {
        self.requiredLbl = UILabel(frame: CGRect(x: self.frame.width - 30, y: 30, width: 20, height: 20))
        self.styleRequiredLabel()
        self.addSubview(self.requiredLbl!)
    }

    func styleRequiredLabel(color:UIColor?, text:String?, font:UIFont?) {
        self.requiredColor = color ?? self.requiredColor
        self.requiredText = text ?? self.requiredText
        self.requiredFont = font ?? self.requiredFont
        self.styleRequiredLabel()
    }

    private func styleRequiredLabel() {
        self.requiredLbl?.textColor = self.requiredColor
        self.requiredLbl?.text = self.requiredText
        self.requiredLbl?.font = self.requiredFont
    }

}

自定义 UITextField

import Foundation
import UIKit

@IBDesignable open class CustomTextField: UITextField {

    @IBInspectable public var borderWidth: CGFloat = 2.0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable public var borderColor: UIColor = UIColor.lightGray {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }

    @IBInspectable public var cornerRadius: CGFloat = 4.0 {
        didSet {
            layer.cornerRadius = cornerRadius
            layer.masksToBounds = true
        }
    }

}

我希望那个自定义 UITextField 也是一个 ValidationView。我知道我可以做一个协议和扩展,然后让我的 CustomTextField 实现该协议,但这不允许初始化覆盖。我宁愿不必更改实现 ValidationView 的视图的初始化。

可以使用@arturdev 回答来完成这样的事情。我最终得到了这个:

import UIKit

class ValidatableProperties {
    var required:Bool
    var validRegex:String?
    var requiredColor:UIColor
    var requiredText:String
    var requiredFont:UIFont

    init(required:Bool, validRegex:String?, requiredColor:UIColor, requiredText:String, requiredFont:UIFont) {
        self.required = required
        self.validRegex = validRegex
        self.requiredText = requiredText
        self.requiredColor = requiredColor
        self.requiredFont = requiredFont
    }
}

protocol Validatable : UIView {

    var validatableProperties:ValidatableProperties! { get set }
    var requiredLbl:UILabel! { get set }

    func setupValidationDefaults()
    func setupValidationViews(frame:CGRect)
    func styleRequiredLabel(color:UIColor?, text:String?, font:UIFont?)

}

extension Validatable {

    func setupValidationDefaults() {
        let props = ValidatableProperties(required: false, validRegex: nil, requiredColor: UIColor.red, requiredText: "*", requiredFont: UIFont.systemFont(ofSize: 16.0, weight: .bold))
        self.validatableProperties = props
    }

    func setupValidationViews(frame:CGRect) {
        self.requiredLbl = UILabel(frame: CGRect(x: frame.width, y: 0, width: 20, height: 20))
        self.styleRequiredLabel()
        self.addSubview(self.requiredLbl)
    }

    func styleRequiredLabel(color:UIColor?, text:String?, font:UIFont?) {
        self.validatableProperties.requiredColor = color ?? self.validatableProperties.requiredColor
        self.validatableProperties.requiredText = text ?? self.validatableProperties.requiredText
        self.validatableProperties.requiredFont = font ?? self.validatableProperties.requiredFont
        self.styleRequiredLabel()
    }

    private func styleRequiredLabel() {
        self.requiredLbl.textColor = self.validatableProperties.requiredColor
        self.requiredLbl.text = self.validatableProperties.requiredText
        self.requiredLbl.font = self.validatableProperties.requiredFont
    }

}

open class ValidationTextField:UITextField, Validatable {
    var requiredLbl: UILabel!
    var validatableProperties: ValidatableProperties!


    override public init(frame: CGRect) {
        super.init(frame: frame)
        self.setupValidationDefaults()
        self.setupValidationViews(frame: frame)
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.setupValidationDefaults()
        self.setupValidationViews(frame: self.frame)
    }

}

但这需要将您想要验证的所有类扩展为它们自己的自定义类,每次都需要覆盖 inits 并调用方法。它可以工作,但并不理想,虽然不完全是反模式继承,但肯定有一些代码味道。

标签: swiftinheritanceuiview

解决方案


您应该将ValidationView其作为协议而不是类,并使您的自定义类符合该协议。

ValidatableView.swift

import UIKit

fileprivate var requiredColor = UIColor.red
fileprivate var requiredText  = "*"
fileprivate var requiredFont  = UIFont.systemFont(ofSize: 16.0, weight: UIFont.Weight.bold)

fileprivate struct AssociatedKeys {
    static var lblKey = "_lblKey_"
}

protocol ValidatableView: class {
    var required: Bool {get}
    var validRegex: String? {get}
    var requiredLbl: UILabel? {get}
}

extension ValidatableView where Self: UIView {
    var required: Bool {
        return false
    }

    var validRegex: String? {
        return nil
    }

    var requiredLbl: UILabel? {
        get {
            return objc_getAssociatedObject(self, &AssociatedKeys.lblKey) as? UILabel
        }
        set {
            objc_setAssociatedObject(self, &AssociatedKeys.lblKey, newValue, .OBJC_ASSOCIATION_RETAIN)
        }
    }

    func setupValidation() {
        self.requiredLbl = UILabel(frame: CGRect(x: self.frame.width - 30, y: 30, width: 20, height: 20))
        self.requiredLbl?.autoresizingMask = .flexibleWidth
        self.styleRequiredLabel()
        self.addSubview(self.requiredLbl!)
    }

    func styleRequiredLabel(color:UIColor? = requiredColor, text:String? = requiredText, font:UIFont? = requiredFont) {
        self.requiredLbl?.textColor = requiredColor
        self.requiredLbl?.text = requiredText
        self.requiredLbl?.font = requiredFont
    }
}

CustomTextField.swift

@IBDesignable open class CustomTextField: UITextField {

    @IBInspectable public var borderWidth: CGFloat = 2.0 {
        didSet {
            layer.borderWidth = borderWidth
        }
    }

    @IBInspectable public var borderColor: UIColor = UIColor.lightGray {
        didSet {
            layer.borderColor = borderColor.cgColor
        }
    }

    @IBInspectable public var cornerRadius: CGFloat = 4.0 {
        didSet {
            layer.cornerRadius = cornerRadius
            layer.masksToBounds = true
        }
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        setupValidation()
    }

    public required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setupValidation()
    }
}

extension CustomTextField: ValidatableView { //<- Magic line :)

}

推荐阅读