Подтвердить что ты не робот

Создание пользовательского анимационного свойства

В UIView вы можете изменить анимированный backgroundColour. А на UISlideView вы можете изменить значение анимированного.

Можете ли вы добавить настраиваемое свойство в свой собственный подкласс UIView, чтобы его можно было анимировать?

Если у меня есть CGPath внутри моего UIView, тогда я смогу анимировать его, изменив процент рисования пути.

Можно ли инкапсулировать эту анимацию в подкласс.

то есть. У меня есть UIView с CGPath, который создает круг.

Если круг отсутствует, он представляет 0%. Если круг заполнен, он представляет 100%. Я могу нарисовать любое значение, изменив процентное соотношение пути. Я также могу анимировать изменение (в подклассе UIView), анимируя процент CGPath и перерисовывая путь.

Могу ли я установить какое-либо свойство (т.е. процентное соотношение) на UIView, чтобы я мог вставить это изменение в блок UIView animateWithDuration и он оживил изменение процента пути?

Надеюсь, я объяснил, что я хотел бы сделать хорошо.

По существу, все, что я хочу сделать, это что-то вроде...

[UIView animateWithDuration:1.0
 animations:^{
     myCircleView.percentage = 0.7;
 }
 completion:nil];

а круговой путь - к данному проценту.

4b9b3361

Ответ 1

Если вы расширяете CALayer и реализуете свой собственный

- (void) drawInContext:(CGContextRef) context

Вы можете сделать анимированное свойство, переопределив needsDisplayForKey (в вашем пользовательском классе CALayer) следующим образом:

+ (BOOL) needsDisplayForKey:(NSString *) key {
    if ([key isEqualToString:@"percentage"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}

Конечно, вам также нужно иметь @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"];

Oh и использовать yourCustomLayer с myCircleView, выполните следующее:

[myCircleView.layer addSublayer:myCustomLayer];

Ответ 2

Завершите пример Swift 3:

Final product

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 = (action.copy() as? CABasicAnimation) {
            animation.keyPath = #keyPath(CircularProgressLayer.progress)
            animation.fromValue = progressLayer.progress
            animation.toValue = progress
            self.layer.add(animation, forKey: #keyPath(CircularProgressLayer.progress))
            return animation
        }
        return super.action(for: layer, forKey: event)
    }

}



/*
* 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


    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)

Здесь есть отличная статья here, в которой подробно рассказывается, как это работает

А также проект objc, который использует те же понятия здесь:

По существу, действие (для layer :) будет вызвано, когда объект анимируется из блока анимации, мы можем запустить нашу собственную анимацию с теми же свойствами (украденную из свойства backgroundColor) и анимировать изменения.

Ответ 3

Для тех, кому нужна более подробная информация о том, что я сделал:

есть прохладный пример от Apple, охватывающий этот вопрос.

например. благодаря этому я обнаружил, что на самом деле вам не нужно добавлять свой пользовательский слой в качестве подуровня (как предлагает @Tom van Zummeren). Вместо этого достаточно добавить метод класса в класс View:

+ (Class)layerClass
{
    return [CustomLayer class];
}

Надеюсь, это поможет кому-то.

Ответ 4

вам придется самому реализовать процентную часть. вы можете переопределить код рисования слоя, чтобы нарисовать ваш cgpath, увеличивая его до установленного значения процента. ознакомьтесь с основным руководством по программированию анимации и типами анимации и руководством по синхронизации