在我的项目中,我有多个具有不同颜色以及不同起点和终点的渐变视图。一些渐变视图还具有带有圆角半径的阴影。
因此,我为每种渐变要求创建了多种视图类型。每个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
}
}
如何使用面向协议的编程和泛型来减少重复的代码?
答案 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
并设置UIView
,storyboard
,{{1} },DesignableView
。
编辑1:
您可以根据需要创建gradientColor1
并为gradientColor2
制作多个gradientStartPoint
,即
gradientEndPoint
用法:
enum Gradient