使用 UIBezierPath 绘制闪烁箭头
Blink Arrow draw with UIBezierPath
我有一个 UIScrollView
,里面有很长的文字。我想通知用户它有更多的内容可以阅读。因此,我在其底部添加了一个带有 UIBezierPath
的箭头。
class ArrowView: UIView {
var arrowPath: UIBezierPath!
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
self.drawArrow(from: CGPoint(x: rect.midX, y: rect.minY), to: CGPoint(x: rect.midX, y: rect.maxY),
tailWidth: 10, headWidth: 25, headLength: 20)
//arrowPath.fill()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func drawArrow(from start: CGPoint, to end: CGPoint, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat){
let length = hypot(end.x - start.x, end.y - start.y)
let tailLength = length - headLength
func p(_ x: CGFloat, _ y: CGFloat) -> CGPoint { return CGPoint(x: x, y: y) }
let points: [CGPoint] = [
p(0, tailWidth / 2),
p(tailLength, tailWidth / 2),
p(tailLength, headWidth / 2),
p(length, 0),
p(tailLength, -headWidth / 2),
p(tailLength, -tailWidth / 2),
p(0, -tailWidth / 2)
]
let cosine = (end.x - start.x) / length
let sine = (end.y - start.y) / length
let transform = CGAffineTransform(a: cosine, b: sine, c: -sine, d: cosine, tx: start.x, ty: start.y)
let path = CGMutablePath()
path.addLines(between: points, transform: transform)
path.closeSubpath()
arrowPath = UIBezierPath.init(cgPath: path)
}
}
我的问题:如何在箭头上实现闪烁动画。假设它有一个从白色到蓝色的渐变层。它应该从白色到蓝色开始,然后蓝色应该出现在起点,然后白色应该开始出现在箭头的终点,这个圆圈应该继续。
最终,此动画应告知用户他们可以滚动视图。
我怎样才能做到这一点?
您可以将箭头放在 UIView 对象中。然后,您可以将 UIView 放在计时器上,并在其中正确更改 alpha。然而,根据闪烁或淡入淡出,您可以使用 UIView 动画来完成您想要的动画。
我说的是uiview,不用scrollview,因为不想隐藏scrollview。所以你会把它放在一个视图中,其中只有 bezierpath 的容器。
您可以尝试将 CAGradientLayer 与 CAAnimation 一起使用。为您的视图添加渐变:
override func viewDidAppear(animated: Bool) {
self.gradient = CAGradientLayer()
self.gradient?.frame = self.view.bounds
self.gradient?.colors = [ UIColor.white.cgColor, UIColor.white.cgColor]
self.view.layer.insertSublayer(self.gradient, atIndex: 0)
animateLayer()
}
func animateLayer(){
var fromColors = self.gradient.colors
var toColors: [AnyObject] = [UIColor.blue.cgColor,UIColor.blue.cgColor]
self.gradient.colors = toColors
var animation : CABasicAnimation = CABasicAnimation(keyPath: "colors")
animation.fromValue = fromColors
animation.toValue = toColors
animation.duration = 3.00
animation.isRemovedOnCompletion = true
animation.fillMode = CAMediaTimingFillMode.forwards
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.delegate = self
self.gradient?.addAnimation(animation, forKey:"animateGradient")
}
并以循环顺序继续渐变动画,例如:
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
self.toColors = self.fromColors;
self.fromColors = self.gradient?.colors
animateLayer()
}
我用CABasicAnimation()
解决了。这首先将渐变层添加到自定义 UIView
然后在其上应用蒙版然后添加动画。
override func draw(_ rect: CGRect) {
// Drawing code
self.drawArrow(from: CGPoint(x: rect.midX, y: rect.minY), to: CGPoint(x: rect.midX, y: rect.maxY),
tailWidth: 10, headWidth: 20, headLength: 15)
//arrowPath.fill()
let startColor = UIColor(red:0.87, green:0.87, blue:0.87, alpha:1.0).cgColor
let finishColor = UIColor(red:0.54, green:0.54, blue:0.57, alpha:1.0).withAlphaComponent(0.5).cgColor
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [startColor,finishColor]
let shapeMask = CAShapeLayer()
shapeMask.path = arrowPath.cgPath
gradient.mask = shapeMask
let animation = CABasicAnimation(keyPath: "colors")
animation.fromValue = [startColor, finishColor]
animation.toValue = [finishColor, startColor]
animation.duration = 2.0
animation.autoreverses = true
animation.repeatCount = Float.infinity
//add the animation to the gradient
gradient.add(animation, forKey: nil)
self.layer.addSublayer(gradient)
}
我有一个 UIScrollView
,里面有很长的文字。我想通知用户它有更多的内容可以阅读。因此,我在其底部添加了一个带有 UIBezierPath
的箭头。
class ArrowView: UIView {
var arrowPath: UIBezierPath!
// Only override draw() if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func draw(_ rect: CGRect) {
// Drawing code
self.drawArrow(from: CGPoint(x: rect.midX, y: rect.minY), to: CGPoint(x: rect.midX, y: rect.maxY),
tailWidth: 10, headWidth: 25, headLength: 20)
//arrowPath.fill()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.darkGray
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func drawArrow(from start: CGPoint, to end: CGPoint, tailWidth: CGFloat, headWidth: CGFloat, headLength: CGFloat){
let length = hypot(end.x - start.x, end.y - start.y)
let tailLength = length - headLength
func p(_ x: CGFloat, _ y: CGFloat) -> CGPoint { return CGPoint(x: x, y: y) }
let points: [CGPoint] = [
p(0, tailWidth / 2),
p(tailLength, tailWidth / 2),
p(tailLength, headWidth / 2),
p(length, 0),
p(tailLength, -headWidth / 2),
p(tailLength, -tailWidth / 2),
p(0, -tailWidth / 2)
]
let cosine = (end.x - start.x) / length
let sine = (end.y - start.y) / length
let transform = CGAffineTransform(a: cosine, b: sine, c: -sine, d: cosine, tx: start.x, ty: start.y)
let path = CGMutablePath()
path.addLines(between: points, transform: transform)
path.closeSubpath()
arrowPath = UIBezierPath.init(cgPath: path)
}
}
我的问题:如何在箭头上实现闪烁动画。假设它有一个从白色到蓝色的渐变层。它应该从白色到蓝色开始,然后蓝色应该出现在起点,然后白色应该开始出现在箭头的终点,这个圆圈应该继续。
最终,此动画应告知用户他们可以滚动视图。
我怎样才能做到这一点?
您可以将箭头放在 UIView 对象中。然后,您可以将 UIView 放在计时器上,并在其中正确更改 alpha。然而,根据闪烁或淡入淡出,您可以使用 UIView 动画来完成您想要的动画。
我说的是uiview,不用scrollview,因为不想隐藏scrollview。所以你会把它放在一个视图中,其中只有 bezierpath 的容器。
您可以尝试将 CAGradientLayer 与 CAAnimation 一起使用。为您的视图添加渐变:
override func viewDidAppear(animated: Bool) {
self.gradient = CAGradientLayer()
self.gradient?.frame = self.view.bounds
self.gradient?.colors = [ UIColor.white.cgColor, UIColor.white.cgColor]
self.view.layer.insertSublayer(self.gradient, atIndex: 0)
animateLayer()
}
func animateLayer(){
var fromColors = self.gradient.colors
var toColors: [AnyObject] = [UIColor.blue.cgColor,UIColor.blue.cgColor]
self.gradient.colors = toColors
var animation : CABasicAnimation = CABasicAnimation(keyPath: "colors")
animation.fromValue = fromColors
animation.toValue = toColors
animation.duration = 3.00
animation.isRemovedOnCompletion = true
animation.fillMode = CAMediaTimingFillMode.forwards
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
animation.delegate = self
self.gradient?.addAnimation(animation, forKey:"animateGradient")
}
并以循环顺序继续渐变动画,例如:
override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
self.toColors = self.fromColors;
self.fromColors = self.gradient?.colors
animateLayer()
}
我用CABasicAnimation()
解决了。这首先将渐变层添加到自定义 UIView
然后在其上应用蒙版然后添加动画。
override func draw(_ rect: CGRect) {
// Drawing code
self.drawArrow(from: CGPoint(x: rect.midX, y: rect.minY), to: CGPoint(x: rect.midX, y: rect.maxY),
tailWidth: 10, headWidth: 20, headLength: 15)
//arrowPath.fill()
let startColor = UIColor(red:0.87, green:0.87, blue:0.87, alpha:1.0).cgColor
let finishColor = UIColor(red:0.54, green:0.54, blue:0.57, alpha:1.0).withAlphaComponent(0.5).cgColor
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [startColor,finishColor]
let shapeMask = CAShapeLayer()
shapeMask.path = arrowPath.cgPath
gradient.mask = shapeMask
let animation = CABasicAnimation(keyPath: "colors")
animation.fromValue = [startColor, finishColor]
animation.toValue = [finishColor, startColor]
animation.duration = 2.0
animation.autoreverses = true
animation.repeatCount = Float.infinity
//add the animation to the gradient
gradient.add(animation, forKey: nil)
self.layer.addSublayer(gradient)
}