什么是创建具有不同起点和终点的多个渐变视图的最佳方法

时间:2019-06-21 13:32:14

标签: ios swift

在我的项目中,我有多个具有不同颜色以及不同起点和终点的渐变视图。一些渐变视图还具有带有圆角半径的阴影。

因此,我为每种渐变要求创建了多种视图类型。每个GradiantView在多个地方使用。

例如:

struct ApplyGradiantView {
    var GradiantLayer : CAGradientLayer?
    init(frame:CGRect,Colors:[CGColor],startPoint:CGPoint,endPoint:CGPoint) {
        GradiantLayer = CAGradientLayer()
        GradiantLayer?.colors = Colors
        GradiantLayer?.startPoint = startPoint
        GradiantLayer?.endPoint = endPoint
        GradiantLayer?.frame = frame
    }
}

class blueGradiantView : UIView {

    var gradiant1Colour = AppColor.gradiantColor1.cgColor
    var gradiant2Colour = AppColor.gradiantColor2.cgColor

    var renderOnese = false

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

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

    }

    override func layoutSubviews() {
        if !renderOnese {
            ApplyCustomeView()
            renderOnese = true
        }
    }

    func ApplyCustomeView() {
        let GradiantLayer = ApplyGradiantView(frame: self.bounds, Colors: [gradiant1Colour,gradiant2Colour], startPoint: CGPoint(x: 0.0, y: 0.0), endPoint: CGPoint(x: 0.8, y: 1.0))
        if let gradiantLayer = GradiantLayer.GradiantLayer {
            self.layer.insertSublayer(gradiantLayer, at: 0)
        }
    }
}

class RedGradiantView : UIView {
    var inerGradiantView = UIView()

    var gradiant1Colour = AppColor.gradiantColor3.cgColor
    var gradiant2Colour = AppColor.gradiantColor4.cgColor
    var renderOnese = false

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

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

    override func layoutSubviews() {
        if !renderOnese {
            ApplyCustomeView()
            //            renderOnese = true
        }
    }

    private func ApplyCustomeView() {
        inerGradiantView.frame = self.bounds.insetBy(dx: 0, dy: 0)
        self.insertSubview(inerGradiantView, at: 0)
        let GradiantLayer = ApplyGradiantView(frame: self.bounds, Colors: [gradiant1Colour,gradiant2Colour], startPoint: CGPoint(x: 0.0, y: 0.0), endPoint: CGPoint(x: 0.8, y: 1.0))
        if let gradiantLayer = GradiantLayer.GradiantLayer {
            inerGradiantView.layer.insertSublayer(gradiantLayer, at: 0)
        }
        inerGradiantView.layer.cornerRadius = 5
        inerGradiantView.clipsToBounds = true
        self.layer.cornerRadius = 5
        self.clipsToBounds = true
        self.backgroundColor = UIColor.white.withAlphaComponent(0.2)
    }

}

class ShadowWithRedBluer : UIView {

    var inerGradiantView = UIView()

    var gradiant1Colour = AppColor.gradiantColor3.cgColor
    var gradiant2Colour = AppColor.gradiantColor4.cgColor
    var renderOnese = false

    private var shadowLayer: CAShapeLayer!

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

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

    override func layoutSubviews() {
        if !renderOnese {
            ApplyCustomeView()
//            renderOnese = true
        }
    }

   private func ApplyCustomeView() {
        inerGradiantView.frame = self.bounds.insetBy(dx: 2, dy: 2)
        self.insertSubview(inerGradiantView, at: 0)
        let GradiantLayer = ApplyGradiantView(frame: self.bounds, Colors: [gradiant1Colour,gradiant2Colour], startPoint: CGPoint(x: 0.0, y: 0.0), endPoint: CGPoint(x: 0.8, y: 1.0))
        if let gradiantLayer = GradiantLayer.GradiantLayer {
            inerGradiantView.layer.insertSublayer(gradiantLayer, at: 0)
        }
        inerGradiantView.layer.cornerRadius = 20
        inerGradiantView.clipsToBounds = true

        self.layer.applySketchShadow(color: UIColor.black, alpha: 0.15, x: 0, y: 50, blur: 50, spread: 0)

        self.backgroundColor = UIColor.clear
    }        
}

extension CALayer {
    func applySketchShadow(
        color: UIColor = .black,
        alpha: Float = 0.5,
        x: CGFloat = 0,
        y: CGFloat = 2,
        blur: CGFloat = 4,
        spread: CGFloat = 0)
    {
        shadowColor = color.cgColor
        shadowOpacity = alpha
        shadowOffset = CGSize(width: x, height: y)
        shadowRadius = blur / 2.0
        if spread == 0 {
            shadowPath = nil
        } else {
            let dx = -spread
            let rect = bounds.insetBy(dx: dx, dy: dx)
            shadowPath = UIBezierPath(rect: rect).cgPath
        }
    }
}

class ShadowOnlyView : UIView {

    var renderOnese = false

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

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

    override func layoutSubviews() {
        if !renderOnese {
            ApplyCustomeView()
            //            renderOnese = true
        }
    }

    private func ApplyCustomeView() {
        self.layer.applySketchShadow(color: UIColor.black, alpha: 0.15, x: 0, y: 50, blur: 50, spread: 0)
        self.backgroundColor = UIColor.white.withAlphaComponent(0.5)
        self.layer.cornerRadius = 10
        self.clipsToBounds = true
    }

}

如何使用面向协议的编程和泛型来减少重复的代码?

2 个答案:

答案 0 :(得分:2)

您问:

  

如何使用面向协议的编程和泛型来减少重复的代码?

尽管我在下面为您展示示例,但TL; DR的答案是,您应该坚持UIKit的面向对象模式,而不是面向协议的编程和泛型。您想加入UIKit机制,而POP和泛型将带来更多的问题,而不仅仅是它们的价值。

因此,我建议使用GradientView的{​​{1}}子类来完成所有渐变工作。然后,如果您想使用一组标准的颜色来重用它(例如,重复使用UIView和重复使用RedGradientView,请从BlueGradientView定义子类,而不会重新实现渐变东西,但只是相应地更新颜色。)

所以我可以这样定义GradientView

GradientView

请注意以下几点:

  • 我没有将渐变层添加为子层并在@IBDesignable class GradientView: UIView { // Inspectables @IBInspectable var startColor: UIColor = .white { didSet { updateGradient() } } @IBInspectable var endColor: UIColor = .blue { didSet { updateGradient() } } @IBInspectable var startPoint: CGPoint = CGPoint(x: 0.5, y: 0) { didSet { updateGradient() } } @IBInspectable var endPoint: CGPoint = CGPoint(x: 0.5, y: 1) { didSet { updateGradient() } } // UIView gradient layers override static var layerClass: AnyClass { return CAGradientLayer.self } var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer } // initialization methods override init(frame: CGRect = .zero) { super.init(frame: frame) updateGradient() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) updateGradient() } } private extension GradientView { func updateGradient() { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] gradientLayer.startPoint = startPoint gradientLayer.endPoint = endPoint } } 中对其进行更新,而是将视图的layerClass定义为layoutSubviews,然后您将无需担心CAGradientLayer。这似乎是次要的观察,但是如果您曾经使用上面的layoutSubviews方法来动画化视图的大小调整动画,则可以在动画过程中正确地呈现渐变。如果您使用layerClass方法,则会使梯度的调整大小发生变化。

  • 我做了这个layoutSubviews。您无需执行此操作,但是如果您想在IB中正确呈现此效果,则很有用。

  • 请注意,我避免实现自定义@IBDesignable方法。您确实希望保留在已建立的init初始化程序中(这样您就可以在情节提要中使用它们,而不必使用非标准的初始化方法来填充代码,等等。)

  • 一个非常小的观察,但是我建议您始终使用小写字母开头您的属性和变量(例如,使用UIView代替GradientLayer。同样,我建议您始终以大写字母开头您的班级名称(例如,我将其命名为gradientLayer而不是blueGradiantView)。

  • 虽然我将在下面向您展示各种红色和蓝色类别,但我建议不要将颜色的实际名称包含在类别名称中。如果您更改颜色主题,则实际上并不需要更改类名。大声笑。

    我还建议,如果针对iOS 11及更高版本,则放弃使用BlueGradiantView类型,而改用命名颜色(您既可以编程方式访问,也可以直接在IB中访问)。 / p>

无论如何,如果您确实想要AppColor类型,它可能看起来像:

BlueGradientView

@IBDesignable class BlueGradientView: GradientView { override init(frame: CGRect = .zero) { super.init(frame: frame) configure() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() } } private extension BlueGradientView { func configure() { startColor = AppColor.gradientColor1 endColor = AppColor.gradientColor2 } } 同样如此:

RedGradientView

还有一个@IBDesignable class RedGradientView: GradientView { override init(frame: CGRect = .zero) { super.init(frame: frame) configure() } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) configure() } } private extension RedGradientView { func configure() { startColor = AppColor.gradientColor3 endColor = AppColor.gradientColor4 } }

RoundedRedWithShadowGradientView

您可能会有这样的扩展名:

@IBDesignable
class RoundedRedWithShadowGradientView: RedGradientView {
    override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        configure()
    }

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

private extension RoundedRedWithShadowGradientView {
    func configure() {
        applyShadow()
        layer.cornerRadius = 10
    }
}

请注意,如果可以的话,我会避免使用extension UIView { func applyShadow(color: UIColor = .black, alpha: Float = 0.5, shadowOffset: CGSize = CGSize(width: 0, height: 2), blur: CGFloat = 4) { layer.shadowColor = color.cgColor layer.shadowOpacity = alpha layer.shadowOffset = shadowOffset layer.shadowRadius = blur / 2.0 } } (它简化了动画,避免了实现shadowPath)。但是随便你怎么做。但是请确保layoutSubviews方法提供的一些重要实用工具是您不希望仅通过拐角半径实现的,因为您不想拥有不必要的脆弱解决方案。

现在,我怀疑您将阅读所有这些内容并说:“但是我想使用面向协议的模式和泛型”,但是尽管这些都是Swift的强大功能,但您不应该与UIKit固有的OOP设计作斗争。在后台,UIKit仍然是Objective-C,仅Swift模式将无法提供您真正想要的互操作性。也许当您转移到SwiftUI时,您可以重新访问它,但是只要您使用的是UIKit,请坚持使用UIKit的本机OOP模式。

答案 1 :(得分:1)

方法1

您可以创建struct的{​​{1}}并为其创建gradient,而不是创建用于添加extension的{​​{1}},即

UIView

用法:

addGradient(colors:start:end:)

方法2

如果您通过extension UIView { func addGradient(colors: [CGColor], start: CGPoint, end: CGPoint) { let gradientLayer = CAGradientLayer() gradientLayer.colors = colors gradientLayer.startPoint = start gradientLayer.endPoint = end gradientLayer.frame = self.bounds self.layer.addSublayer(gradientLayer) } } 进行操作,则可以使用self.view.addGradient(colors: [UIColor.blue.cgColor, UIColor.white.cgColor], start: .zero, end: CGPoint(x: 0, y: 1)) storyboard通过@IBDesignable本身进行更改,即

@IBInspectable

只需将storyboard@IBDesignable class DesignableView: UIView { @IBInspectable var gradientColor1: UIColor = UIColor.white { didSet{ self.setGradient() } } @IBInspectable var gradientColor2: UIColor = UIColor.white { didSet{ self.setGradient() } } @IBInspectable var gradientStartPoint: CGPoint = .zero { didSet{ self.setGradient() } } @IBInspectable var gradientEndPoint: CGPoint = CGPoint(x: 0, y: 1) { didSet{ self.setGradient() } } private func setGradient() { let gradientLayer = CAGradientLayer() gradientLayer.colors = [self.gradientColor1.cgColor, self.gradientColor2.cgColor] gradientLayer.startPoint = self.gradientStartPoint gradientLayer.endPoint = self.gradientEndPoint gradientLayer.frame = self.bounds if let topLayer = self.layer.sublayers?.first, topLayer is CAGradientLayer { topLayer.removeFromSuperlayer() } self.layer.addSublayer(gradientLayer) } } 的{​​{1}}的{​​{1}}设置为class并设置UIViewstoryboard,{{1} },DesignableView

编辑1:

您可以根据需要创建gradientColor1并为gradientColor2制作多个gradientStartPoint,即

gradientEndPoint

用法:

enum Gradient