我想为自定义视图的背景颜色设置动画。
我的绘图代码非常简单。 draw(in:)
方法被覆盖如下:
@IBDesignable
class SquareView: UIView {
override func draw(_ rect: CGRect) {
super.draw(rect)
let strokeWidth = self.width / 8
let path = UIBezierPath()
path.move(to: CGPoint(x: self.width - strokeWidth / 2, y: 0))
path.addLine(to: CGPoint(x: self.width - strokeWidth / 2, y: self.height - strokeWidth / 2))
path.addLine(to: CGPoint(x: 0, y: self.height - strokeWidth / 2))
self.backgroundColor?.darker().setStroke()
path.lineWidth = strokeWidth
path.stroke()
}
}
darker
方法只返回调用它的颜色的较暗版本。
当我将背景设置为蓝色时,它会绘制如下内容:
我想为其背景颜色设置动画,使其逐渐变为红色。这将是最终结果:
我第一次尝试:
UIView.animate(withDuration: 1) {
self.square.backgroundColor = .red
}
它只是瞬间将颜色变为红色,没有动画。
然后我研究并发现我需要使用CALayer
s。因此,我尝试分层绘制事物:
@IBDesignable
class SquareViewLayers: UIView {
dynamic var squareColor: UIColor = .blue {
didSet {
setNeedsDisplay()
}
}
func setupView() {
layer.backgroundColor = squareColor.cgColor
let sublayer = CAShapeLayer()
let path = UIBezierPath()
let strokeWidth = self.width / 8
path.move(to: CGPoint(x: self.width - strokeWidth / 2, y: 0))
path.addLine(to: CGPoint(x: self.width - strokeWidth / 2, y: self.height - strokeWidth / 2))
path.addLine(to: CGPoint(x: 0, y: self.height - strokeWidth / 2))
self.tintColor.darker().setStroke()
path.lineWidth = strokeWidth
sublayer.path = path.cgPath
sublayer.strokeColor = squareColor.darker().cgColor
sublayer.lineWidth = strokeWidth
sublayer.backgroundColor = UIColor.clear.cgColor
layer.addSublayer(sublayer)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
}
但结果太可怕了
我使用此代码尝试设置动画:
let anim = CABasicAnimation(keyPath: "squareColor")
anim.fromValue = UIColor.blue
anim.toValue = UIColor.red
anim.duration = 1
square.layer.add(anim, forKey: nil)
但是没有任何事情发生。
我很困惑。这样做的正确方法是什么?
编辑:
darker
方法来自名为SwiftyColor
的可可荚。这就是它的实现方式:
// in an extension of UIColor
public func darker(amount: CGFloat = 0.25) -> UIColor {
return hueColor(withBrightnessAmount: 1 - amount)
}
private func hueColor(withBrightnessAmount amount: CGFloat) -> UIColor {
var hue: CGFloat = 0
var saturation: CGFloat = 0
var brightness: CGFloat = 0
var alpha: CGFloat = 0
if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
return UIColor(hue: hue, saturation: saturation,
brightness: brightness * amount,
alpha: alpha)
}
return self
}
答案 0 :(得分:2)
您使用- -5
开启了正确的轨道。
CABasicAnimation
中的keyPath:
应为let anim = CABasicAnimation(keyPath: "squareColor")
。
backgroundColor
和anim.fromValue
需要anim.toValue
个值(因为您在使用CGColor
的{{1}}上操作。您可以在此处使用CALayer
。
请确保此动画不会为您保留颜色更改,因此您可能需要手动更改该属性。
答案 1 :(得分:1)
使用CAKeyframeAnimation
您需要为backgroundColor
属性设置动画:
class SquareView: UIView
{
let sublayer = CAShapeLayer()
required init?(coder aDecoder: NSCoder)
{
super.init(coder: aDecoder)
let frame = self.frame
let strokeWidth = self.frame.width / 8
sublayer.frame = self.bounds
let path = UIBezierPath()
path.move(to: CGPoint(x: frame.width - strokeWidth / 2 , y: 0))
path.addLine(to: CGPoint(x: frame.width - strokeWidth / 2, y: frame.height - strokeWidth / 2))
path.addLine(to: CGPoint(x: 0, y: frame.height - strokeWidth / 2))
path.addLine(to: CGPoint(x: 0 , y: 0))
path.lineWidth = strokeWidth
sublayer.path = path.cgPath
sublayer.fillColor = UIColor.blue.cgColor
sublayer.lineWidth = strokeWidth
sublayer.backgroundColor = UIColor.blue.cgColor
layer.addSublayer(sublayer)
}
//Action
func animateIt()
{
let animateToColor = UIColor(cgColor: sublayer.backgroundColor!).darker().cgColor
animateColorChange(mLayer: self.sublayer, colors: [sublayer.backgroundColor!, animateToColor], duration: 1)
}
func animateColorChange(mLayer:CALayer ,colors:[CGColor], duration:CFTimeInterval)
{
let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
CATransaction.begin()
animation.keyTimes = [0.2, 0.5, 0.7, 1]
animation.values = colors
animation.calculationMode = kCAAnimationPaced
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.repeatCount = 0
animation.duration = duration
mLayer.add(animation, forKey: nil)
CATransaction.commit()
}
}
注意:我编辑了答案,以便形状始终适合从故事板添加的父视图
答案 2 :(得分:1)
目标-C
view.backgroundColor = [UIColor whiteColor].CGColor;
[UIView animateWithDuration:2.0 animations:^{
view.layer.backgroundColor = [UIColor greenColor].CGColor;
} completion:NULL];
夫特
view.backgroundColor = UIColor.white.cgColor
UIView.animate(withDuration: 2.0) {
view.backgroundColor = UIColor.green.cgColor
}
答案 3 :(得分:1)
你可以用半透明的黑色来抚摸L形吗?
然后你不必乱搞重绘和颜色计算,你可以使用基本的UIView.animate()
在“LShadow”下方添加到SquareView上,因此您可以按照惯例设置SquareView背景颜色,使用UIView.animate()
ViewController.swift
import UIKit
class ViewController: UIViewController {
var squareView: SquareView!
override func viewDidLoad() {
super.viewDidLoad()
squareView = SquareView.init(frame: CGRect(x:100, y:100, width:200, height:200))
self.view.addSubview(squareView)
squareView.backgroundColor = UIColor.blue
UIView.animate(withDuration: 2.0, delay: 1.0, animations: {
self.squareView.backgroundColor = UIColor.red
}, completion:nil)
}
}
SquareView.swift
import UIKit
class SquareView: UIView {
func setupView() {
let shadow = LShadow.init(frame: self.bounds)
self.addSubview(shadow)
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
}
class LShadow: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.backgroundColor = UIColor.clear
}
override func draw(_ rect: CGRect) {
let strokeWidth = self.frame.size.width / 8
let path = UIBezierPath()
path.move(to: CGPoint(x: self.frame.size.width - strokeWidth / 2, y: 0))
path.addLine(to: CGPoint(x: self.frame.size.width - strokeWidth / 2, y: self.frame.size.height - strokeWidth / 2))
path.addLine(to: CGPoint(x: 0, y: self.frame.size.height - strokeWidth / 2))
UIColor.init(white: 0, alpha: 0.5).setStroke()
path.lineWidth = strokeWidth
path.stroke()
}
}
答案 4 :(得分:1)
我们在下面的代码中有两个不同的CAShapeLayer,前景和背景。背景是绘制到实际开始倒L的程度的矩形路径。 L是使用简单的UIBezierPath几何创建的路径。在设置颜色后,我将每个图层的更改设置为fillColor。
class SquareViewLayers: UIView, CAAnimationDelegate {
var color: UIColor = UIColor.blue {
didSet {
animate(layer: backgroundLayer,
from: oldValue,
to: color)
animate(layer: foregroundLayer,
from: oldValue.darker(),
to: color.darker())
}
}
let backgroundLayer = CAShapeLayer()
let foregroundLayer = CAShapeLayer()
func setupView() {
foregroundLayer.frame = frame
layer.addSublayer(foregroundLayer)
backgroundLayer.frame = frame
layer.addSublayer(backgroundLayer)
let strokeWidth = width / 8
let backgroundPathRect = CGRect(origin: .zero, size: CGSize(width: width - strokeWidth,
height: height - strokeWidth))
let backgroundPath = UIBezierPath(rect: backgroundPathRect)
backgroundLayer.fillColor = color.cgColor
backgroundLayer.path = backgroundPath.cgPath
let foregroundPath = UIBezierPath()
foregroundPath.move(to: CGPoint(x: backgroundPathRect.width,
y: 0))
foregroundPath.addLine(to: CGPoint(x: width,
y: 0))
foregroundPath.addLine(to: CGPoint(x: width, y: height))
foregroundPath.addLine(to: CGPoint(x: 0,
y: height))
foregroundPath.addLine(to: CGPoint(x: 0,
y: backgroundPathRect.height))
foregroundPath.addLine(to: CGPoint(x: backgroundPathRect.width,
y: backgroundPathRect.height))
foregroundPath.addLine(to: CGPoint(x: backgroundPathRect.width,
y: 0))
foregroundPath.close()
foregroundLayer.path = foregroundPath.cgPath
foregroundLayer.fillColor = color.darker().cgColor
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func animate(layer: CAShapeLayer,
from: UIColor,
to: UIColor) {
let fillColorAnimation = CABasicAnimation(keyPath: "fillColor")
fillColorAnimation.fromValue = from.cgColor
layer.fillColor = to.cgColor
fillColorAnimation.duration = 1.0
layer.add(fillColorAnimation,
forKey: nil)
}
}
这是结果,
答案 5 :(得分:1)
我实现了使用动画更改视图的背景颜色,并在源代码中进行了一些调整,以使用CAShapeLayer添加倒L形状。我实现了所需的动画。这是视图的样本。
class SquareView: UIView, CAAnimationDelegate {
fileprivate func setup() {
self.layer.addSublayer(self.sublayer)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup
}
let sublayer = CAShapeLayer()
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
override func layoutSubviews() {
super.layoutSubviews()
let strokeWidth = self.bounds.width / 8
var frame = self.bounds
frame.size.width -= strokeWidth
frame.size.height -= strokeWidth
self.sublayer.path = UIBezierPath(rect: frame).cgPath
}
var color : UIColor = UIColor.clear {
willSet {
self.sublayer.fillColor = newValue.cgColor
self.backgroundColor = newValue.darker()
}
}
func animation(color: UIColor, duration: CFTimeInterval = 1.0) {
let animation = CABasicAnimation()
animation.keyPath = "backgroundColor"
animation.fillMode = kCAFillModeForwards
animation.duration = duration
animation.repeatCount = 1
animation.autoreverses = false
animation.fromValue = self.backgroundColor?.cgColor
animation.toValue = color.darker().cgColor
animation.delegate = self;
let shapeAnimation = CABasicAnimation()
shapeAnimation.keyPath = "fillColor"
shapeAnimation.fillMode = kCAFillModeForwards
shapeAnimation.duration = duration
shapeAnimation.repeatCount = 1
shapeAnimation.autoreverses = false
shapeAnimation.fromValue = self.sublayer.fillColor
shapeAnimation.toValue = color.cgColor
shapeAnimation.delegate = self;
self.layer.add(animation, forKey: "kAnimation")
self.sublayer.add(shapeAnimation, forKey: "kAnimation")
self.color = color
}
public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
self.layer.removeAnimation(forKey: "kAnimation")
self.sublayer.removeAnimation(forKey: "kAnimation")
}
}
您可以使用方法animation(color: duration:)
在所需的动画持续时间内使用动画更改颜色。
示例强>
let view = SquareView(frame: CGRect(x: 10, y: 100, width: 200, height: 300))
view.color = UIColor.blue
view.animation(color: UIColor.red)
以下是结果