无法让自定义活动指示器设置动画

Posted

技术标签:

【中文标题】无法让自定义活动指示器设置动画【英文标题】:Can't get custom activity indicator to animate 【发布时间】:2020-06-13 12:35:48 【问题描述】:

我将原本在 Objc file 中的 custom activity indicator 重写为 Swift。活动指示器出现在场景中,但动画没有出现。

我需要一些帮助来弄清楚为什么动画没有出现:

vc:

class ViewController: UIViewController 

    fileprivate lazy var customActivityView: CustomActivityView = 
        let customActivityView = CustomActivityView()
        customActivityView.translatesAutoresizingMaskIntoConstraints = false
        customActivityView.delegate = self
        customActivityView.numberOfCircles = 3
        customActivityView.radius = 20
        customActivityView.internalSpacing = 3
        customActivityView.startAnimating()

        return customActivityView
    ()

    override func viewDidLoad() 
        super.viewDidLoad()
        view.backgroundColor = .white

        setAnchors()
    

    fileprivate func setAnchors() 

        view.addSubview(customActivityView)
        customActivityView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
        customActivityView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
        customActivityView.widthAnchor.constraint(equalToConstant: 100).isActive = true
        customActivityView.heightAnchor.constraint(equalToConstant: 100).isActive = true
    


extension ViewController: CustomActivityViewDelegate 

    func activityIndicatorView(activityIndicatorView: CustomActivityView, circleBackgroundColorAtIndex index: Int) -> UIColor 

        let red = CGFloat(Double((arc4random() % 256)) / 255.0)
        let green = CGFloat(Double((arc4random() % 256)) / 255.0)
        let blue = CGFloat(Double((arc4random() % 256)) / 255.0)
        let alpha: CGFloat = 1
        return UIColor(red: red, green: green, blue: blue, alpha: alpha)
    

Swift 文件:

import UIKit

protocol CustomActivityViewDelegate: class 
    func activityIndicatorView(activityIndicatorView: CustomActivityView, circleBackgroundColorAtIndex index: Int) -> UIColor


class CustomActivityView: UIView 

    var numberOfCircles: Int = 0
    var internalSpacing: CGFloat = 0
    var radius: CGFloat = 0
    var delay: CGFloat = 0
    var duration: CFTimeInterval  = 0
    var defaultColor = UIColor.systemPink
    var isAnimating = false

    weak var delegate: CustomActivityViewDelegate?

    override init(frame: CGRect) 
        super.init(frame: frame)

        setupDefaults()
    

    required init?(coder: NSCoder) 
        super.init(coder: coder)
        setupDefaults()
        fatalError("init(coder:) has not been implemented")
    

    func setupDefaults() 
        self.translatesAutoresizingMaskIntoConstraints = false
        numberOfCircles = 5
        internalSpacing = 5
        radius = 10
        delay = 0.2
        duration = 0.8
    

    func createCircleWithRadius(radius: CGFloat, color: UIColor, positionX: CGFloat) -> UIView 
        let circle = UIView(frame: CGRect(x: positionX, y: 0, width: radius * 2, height: radius * 2))
        circle.backgroundColor = color
        circle.layer.cornerRadius = radius
        circle.translatesAutoresizingMaskIntoConstraints = false;
        return circle
    

    func createAnimationWithDuration(duration: CFTimeInterval, delay: CGFloat) -> CABasicAnimation 
        let anim: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation")
        anim.fromValue = 0.0
        anim.toValue = 1.0
        anim.autoreverses = true
        anim.duration = duration
        anim.isRemovedOnCompletion = false
        anim.beginTime = CACurrentMediaTime()+Double(delay)
        anim.repeatCount = .infinity
        anim.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
        return anim;
    

    func addCircles() 

        for i in 0..<numberOfCircles 

            var color: UIColor?

            color = delegate?.activityIndicatorView(activityIndicatorView: self, circleBackgroundColorAtIndex: i)

            let circle = createCircleWithRadius(radius: radius,
                                                color: ((color == nil) ? self.defaultColor : color)!,
                                                positionX: CGFloat(i) * ((2 * self.radius) + self.internalSpacing))

            circle.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)

            circle.layer.add(self.createAnimationWithDuration(duration: self.duration,
                                                              delay: CGFloat(i) * self.delay), forKey: "scale")

            self.addSubview(circle)
        
    

    func removeCircles() 
        self.subviews.forEach( $0.removeFromSuperview() )
    

    @objc func startAnimating() 
        if !isAnimating 
            addCircles()
            self.isHidden = false
            isAnimating = true
        
    

    @objc func stopAnimating() 

        if isAnimating 
            removeCircles()
            self.isHidden = true
            isAnimating = false
        
    

    func intrinsicContentSize() -> CGSize 
        let width: CGFloat = (CGFloat(self.numberOfCircles) * ((2 * self.radius) + self.internalSpacing)) - self.internalSpacing
        let height: CGFloat = radius * 2
        return CGSize(width: width, height: height)
    

    func setNumberOfCircles(numberOfCircles: Int) 
        self.numberOfCircles = numberOfCircles
        self.invalidateIntrinsicContentSize()
    

    func setRadius(radius: CGFloat) 
        self.radius = radius
        self.invalidateIntrinsicContentSize()
    

    func setInternalSpacing(internalSpacing: CGFloat) 
        self.internalSpacing = internalSpacing
        self.invalidateIntrinsicContentSize()
    

【问题讨论】:

【参考方案1】:

我为动画使用了错误的关键路径:

我用过

// incorrect
let anim: CABasicAnimation = CABasicAnimation(keyPath: "transform.rotation")

但我应该用过

// correct
let anim: CABasicAnimation = CABasicAnimation(keyPath: "transform.scale")

【讨论】:

以上是关于无法让自定义活动指示器设置动画的主要内容,如果未能解决你的问题,请参考以下文章

为自定义活动指示器视图使 UIView 半透明

无法让自定义 RKBlockValueTransformer 与 RestKit 一起正常工作

如何让自定义视图观察包含片段的生命周期事件而不是活动?

带有自定义图像的活动指示器

无法让自定义对话框与 DirtyForms 一起使用

让自定义列表适配器正确膨胀多个线性布局