两个答案.. 首先,如果你真的想在每次绘制时调用
drawRect
,你应该使用
CADisplayLink
并手动动画化视图的框架。
其次: 如果你不想这样做但仍然希望它平滑地绘制,则将contentMode设置为scaleAspectFit。这将使它与
UIView.animateWithDuration
平滑地绘制。然而,它不会在每个周期调用
drawRect
,而
CADisplayLink
解决方案会。
使用一个
CADisplayLink
来更新边界框。添加
needsDisplayOnBoundsChange = true
,以便在边界改变时重新绘制视图。
http://i.imgur.com/JjarYsq.gif
![enter image description here](https://istack.dev59.com/8Zx6k.gif)
例子:
import UIKit
class RadioWave : UIView {
private var dl: CADisplayLink?
private var startTime: CFTimeInterval!
private var fromFrame: CGRect!
private var toFrame: CGRect!
private var duration: CFTimeInterval!
private var completion: (() -> Void)?
override init(frame: CGRect) {
super.init(frame: frame)
self.isOpaque = false
self.layer.needsDisplayOnBoundsChange = true;
self.fromFrame = frame;
self.toFrame = frame
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func animateToFrame(frame: CGRect, duration: CFTimeInterval, completion:(() -> Void)? = nil) {
self.dl?.remove(from: .current, forMode: .commonModes)
self.dl?.invalidate()
self.dl = CADisplayLink(target: self, selector: #selector(onUpdate(dl:)))
self.completion = completion
self.fromFrame = self.frame
self.toFrame = frame
self.duration = duration
self.startTime = CACurrentMediaTime()
self.dl?.add(to: .current, forMode: .commonModes)
}
override func draw(_ rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
ctx?.clear(rect)
ctx?.addEllipse(in: rect)
ctx?.clip()
ctx?.setFillColor(UIColor.blue.cgColor)
ctx?.fill(rect)
}
@objc
func onUpdate(dl: CADisplayLink) {
let dt = CGFloat((dl.timestamp - self.startTime) / self.duration)
if (dt > 1.0) {
self.frame = self.toFrame
self.dl?.remove(from: .current, forMode: .commonModes)
self.dl?.invalidate()
self.dl = nil
completion?()
completion = nil
return;
}
var frame: CGRect! = self.toFrame;
frame.origin.x = (self.toFrame.origin.x - self.fromFrame.origin.x) * dt + fromFrame.origin.x
frame.origin.y = (self.toFrame.origin.y - self.fromFrame.origin.y) * dt + fromFrame.origin.y
frame.size.width = (self.toFrame.size.width - self.fromFrame.size.width) * dt + fromFrame.size.width
frame.size.height = (self.toFrame.size.height - self.fromFrame.size.height) * dt + fromFrame.size.height
self.frame = frame
}
}
class ViewController: UIViewController {
var radioWave: RadioWave!
override func viewDidLoad() {
super.viewDidLoad()
self.radioWave = RadioWave(frame: CGRect(x: self.view.center.x - 10.0, y: self.view.center.y - 10.0, width: 20.0, height: 20.0))
self.view.addSubview(self.radioWave)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
let smallFrame = CGRect(x: self.view.center.x - 10.0, y: self.view.center.y - 10.0, width: 20.0, height: 20.0)
let largeFrame = CGRect(x: self.view.center.x - 100.0, y: self.view.center.y - 100.0, width: 200.0, height: 200.0)
self.radioWave.animateToFrame(frame: largeFrame, duration: 2.0, completion: {
self.radioWave.animateToFrame(frame: smallFrame, duration: 2.0)
})
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
通常情况下,只有当视图边界发生变化时才会重新绘制视图。在 iOS 上的动画实际上只是您的视图的快照被插值(呈现层)。这样做可以大大提高性能,而不必在每个动画步骤中重新绘制和布局视图。
使用
[UIView animateWithDuration]
动画绘制一个圆形。
class RadioWave : UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.isOpaque = false
self.layer.needsDisplayOnBoundsChange = true
self.contentMode = .scaleAspectFit
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
let ctx = UIGraphicsGetCurrentContext()
ctx?.clear(rect)
ctx?.addEllipse(in: rect)
ctx?.clip()
ctx?.setFillColor(UIColor.blue.cgColor)
ctx?.fill(rect)
}
}
class ViewController: UIViewController {
var radioWave: RadioWave!
override func viewDidLoad() {
super.viewDidLoad()
self.radioWave = RadioWave(frame: CGRect(x: self.view.center.x - 10.0, y: self.view.center.y - 10.0, width: 20.0, height: 20.0))
self.view.addSubview(self.radioWave)
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
let largeFrame = CGRect(x: self.view.center.x - 100.0, y: self.view.center.y - 100.0, width: 200.0, height: 200.0)
self.radioWave.setNeedsDisplay()
UIView.animate(withDuration: 2.0, delay: 0.0, options: [.layoutSubviews, .autoreverse, .repeat], animations: {
self.radioWave.frame = largeFrame
}, completion: { (completed) in
})
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
上述代码使用了contentMode为scaleAspectFit,这允许动画在缩放时保持清晰而不会模糊!这是我所知道的唯一一种绘制平滑圆形动画的方法。它只会在每个动画的开始和结束时调用 drawRect 方法。