在UIView
上,可以将backgroundColour更改为动画。在UISlideView
上,您可以更改动画的值。
您是否可以将自定义属性添加到您自己的UIView
子类中,以便对其进行动画处理?
如果我在我的UIView
中有一个CGPath
,那么我可以通过改变绘制路径的百分比来对它的绘制进行动画处理。
我可以将动画封装到子类中吗。
例如,我有一个带CGPath
的UIView
,它创建了一个圆。
如果圆圈不在那里,它代表0%。如果圆已满,则表示100%。我可以通过改变路径的绘制百分比来绘制任何值。我还可以通过对CGPath
的百分比进行动画处理并重新绘制路径来实现(在UIView子类中)更改。
我可以在UIView
上设置一些属性(即百分比),这样我就可以将更改粘贴到UIView animateWithDuration
块中,并使路径百分比的更改具有动画效果吗?
我希望我已经解释了我想要做好的事情。
基本上,我想做的就是...
[UIView animateWithDuration:1.0
animations:^{
myCircleView.percentage = 0.7;
}
completion:nil];
并且圆路径动画到给定的百分比。
发布于 2013-01-07 17:59:02
如果您扩展了CALayer并实现了您定制
- (void) drawInContext:(CGContextRef) context
可以通过覆盖(在自定义CALayer类中) needsDisplayForKey
来创建可设置动画的属性,如下所示:
+ (BOOL) needsDisplayForKey:(NSString *) key {
if ([key isEqualToString:@"percentage"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
当然,您还需要一个名为percentage
的@property
。从现在开始,您可以使用核心动画对percentage属性进行动画处理。我没有使用[UIView animateWithDuration...]
调用来检查它是否也可以工作。也许能行得通。但这对我很有效:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"percentage"];
animation.duration = 1.0;
animation.fromValue = [NSNumber numberWithDouble:0];
animation.toValue = [NSNumber numberWithDouble:100];
[myCustomLayer addAnimation:animation forKey:@"animatePercentage"];
哦,要在myCircleView
中使用yourCustomLayer
,请执行以下操作:
[myCircleView.layer addSublayer:myCustomLayer];
发布于 2013-01-07 17:50:57
你将不得不自己实现百分比部分。您可以覆盖图层绘制代码,根据设置的百分比值绘制您的cgpath。查看core animation programming guide和animation types and timing guide
发布于 2020-06-01 22:41:23
@David Rees answer让我走上了正确的道路,但有一个问题。在我的例子中,动画的完成总是在动画开始之后返回false。
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: {
circularProgress.progress = 0.76
}, completion: { finished in
// finished - always false
})
这就是它为我工作的方式-动画的动作是在CALayer内部处理的。
我还包括了一个小例子,如何使层与其他属性,如“颜色”。在这种情况下,如果没有复制值的初始化器,则更改颜色仅对非动画视图有效。在动画期间,它将是可见的“默认设置”。
public class CircularProgressView: UIView {
@objc public dynamic var progress: CGFloat {
get {
return progressLayer.progress
}
set {
progressLayer.progress = newValue
}
}
fileprivate var progressLayer: CircularProgressLayer {
return layer as! CircularProgressLayer
}
override public class var layerClass: AnyClass {
return CircularProgressLayer.self
}
}
/*
* Concepts taken from:
* https://stackoverflow.com/a/37470079
*/
fileprivate class CircularProgressLayer: CALayer {
@NSManaged var progress: CGFloat
let startAngle: CGFloat = 1.5 * .pi
let twoPi: CGFloat = 2 * .pi
let halfPi: CGFloat = .pi / 2
var color: UIColor = .red
// preserve layer properties
// without this specyfic init, if color was changed to sth else
// animation would still use .red
override init(layer: Any) {
super.init(layer: layer)
if let layer = layer as? CircularProgressLayer {
self.color = layer.color
self.progress = layer.progress
}
}
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override class func needsDisplay(forKey key: String) -> Bool {
if key == #keyPath(progress) {
return true
}
return super.needsDisplay(forKey: key)
}
override func action(forKey event: String) -> CAAction? {
if event == #keyPath(CircularProgressLayer.progress) {
guard let animation = action(forKey: #keyPath(backgroundColor)) as? CABasicAnimation else {
setNeedsDisplay()
return nil
}
if let presentation = presentation() {
animation.keyPath = event
animation.fromValue = presentation.value(forKeyPath: event)
animation.toValue = nil
} else {
return nil
}
return animation
}
return super.action(forKey: event)
}
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
UIGraphicsPushContext(ctx)
//Light Gray
UIColor.lightGray.setStroke()
let center = CGPoint(x: bounds.midX, y: bounds.midY)
let strokeWidth: CGFloat = 4
let radius = (bounds.size.width / 2) - strokeWidth
let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: twoPi, clockwise: true)
path.lineWidth = strokeWidth
path.stroke()
// Red - default
self.color.setStroke()
let endAngle = (twoPi * progress) - halfPi
let pathProgress = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle , clockwise: true)
pathProgress.lineWidth = strokeWidth
pathProgress.lineCapStyle = .round
pathProgress.stroke()
UIGraphicsPopContext()
}
}
我在本文中找到的以不同方式处理动画和复制层属性的方法:https://medium.com/better-programming/make-apis-like-apple-animatable-view-properties-in-swift-4349b2244cea
https://stackoverflow.com/questions/14192816
复制相似问题