iphone - una - no se pudo verificar la app tutuapp
Crear una propiedad animable personalizada (4)
Para los que necesitan más detalles sobre eso como yo lo hice:
Hay un buen ejemplo de Apple que cubre esta pregunta.
Por ejemplo, gracias a él, descubrí que no es necesario agregar la capa personalizada como subcapa (como sugiere @Tom van Zummeren ). En su lugar, es suficiente agregar un método de clase a tu clase de Vista:
+ (Class)layerClass
{
return [CustomLayer class];
}
Espero que ayude a alguien.
En UIView
puedes cambiar el UIView
fondo animado. Y en un UISlideView
puedes cambiar el valor animado.
¿Puedes agregar una propiedad personalizada a tu propia subclase UIView
para que pueda ser animada?
Si tengo un CGPath
dentro de mi UIView
entonces puedo animar su dibujo cambiando el porcentaje dibujado de la ruta.
¿Puedo encapsular esa animación en la subclase.
es decir, tengo un UIView
con un CGPath
que crea un círculo.
Si el círculo no está allí, representa el 0%. Si el círculo está lleno, representa el 100%. Puedo dibujar cualquier valor cambiando el porcentaje dibujado de la ruta. También puedo animar el cambio (dentro de la subclase UIView) animando el porcentaje de CGPath
y CGPath
dibujar la ruta.
¿Puedo establecer alguna propiedad (es decir, porcentaje) en la UIView
para poder pegar el cambio en un bloque UIView animateWithDuration
y animar el cambio del porcentaje de la ruta?
Espero haber explicado lo que me gustaría hacer bien.
Esencialmente, todo lo que quiero hacer es algo como ...
[UIView animateWithDuration:1.0
animations:^{
myCircleView.percentage = 0.7;
}
completion:nil];
y el camino del círculo anima al porcentaje dado.
Si extiendes CALayer e implementas tu personalizado
- (void) drawInContext:(CGContextRef) context
Puedes hacer una propiedad animable anulando needsDisplayForKey
(en tu clase CALayer personalizada) de esta manera:
+ (BOOL) needsDisplayForKey:(NSString *) key {
if ([key isEqualToString:@"percentage"]) {
return YES;
}
return [super needsDisplayForKey:key];
}
Por supuesto, también debe tener un percentage
llamado @property
. A partir de ahora, puede animar la propiedad de porcentaje utilizando la animación central. No verifiqué si funciona también usando la [UIView animateWithDuration...]
. Podría funcionar. Pero esto funcionó para mí:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"percentage"];
animation.duration = 1.0;
animation.fromValue = [NSNumber numberWithDouble:0];
animation.toValue = [NSNumber numberWithDouble:100];
[myCustomLayer addAnimation:animation forKey:@"animatePercentage"];
Ah, y para usar yourCustomLayer
con myCircleView
, haz esto:
[myCircleView.layer addSublayer:myCustomLayer];
Tendrá que implementar la parte porcentual usted mismo. puede anular el código de dibujo de capa para dibujar su cgpath según el valor de porcentaje establecido. Verifique la guía de programación de animación principal y los tipos de animación y guía de sincronización.
Ejemplo completo de Swift 3:
public class CircularProgressView: UIView {
public dynamic var progress: CGFloat = 0 {
didSet {
progressLayer.progress = progress
}
}
fileprivate var progressLayer: CircularProgressLayer {
return layer as! CircularProgressLayer
}
override public class var layerClass: AnyClass {
return CircularProgressLayer.self
}
override public func action(for layer: CALayer, forKey event: String) -> CAAction? {
if event == #keyPath(CircularProgressLayer.progress),
let action = action(for: layer, forKey: #keyPath(backgroundColor)) as? CAAnimation {
let animation = CABasicAnimation()
animation.keyPath = #keyPath(CircularProgressLayer.progress)
animation.fromValue = progressLayer.progress
animation.toValue = progress
animation.beginTime = action.beginTime
animation.duration = action.duration
animation.speed = action.speed
animation.timeOffset = action.timeOffset
animation.repeatCount = action.repeatCount
animation.repeatDuration = action.repeatDuration
animation.autoreverses = action.autoreverses
animation.fillMode = action.fillMode
animation.timingFunction = action.timingFunction
animation.delegate = action.delegate
self.layer.add(animation, forKey: #keyPath(CircularProgressLayer.progress))
}
return super.action(for: layer, forKey: event)
}
}
/*
* Concepts taken from:
* https://.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
override class func needsDisplay(forKey key: String) -> Bool {
if key == #keyPath(progress) {
return true
}
return super.needsDisplay(forKey: key)
}
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
UIGraphicsPushContext(ctx)
//Light Grey
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
UIColor.red.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()
}
}
let circularProgress = CircularProgressView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: {
circularProgress.progress = 0.76
}, completion: nil)
Aquí hay un gran artículo de objc, que incluye detalles sobre cómo funciona esto.
Así como un proyecto de objc que usa los mismos conceptos here :
Esencialmente, se llamará a action (for layer :) cuando un objeto se esté animando desde un bloque de animación, podemos comenzar nuestras propias animaciones con las mismas propiedades (robadas de la propiedad backgroundColor) y animar los cambios.