首页 > 解决方案 > 为什么 CAShapeLayer 有时不出现?

问题描述

我在我的应用程序中使用以下自定义 UIView 类作为加载指示器。大多数时候它工作得很好,我可以用loadingView.startLoading()它来让它出现。然而,在极少数情况下,只有 UIView 出现,其背后有阴影,而 CAShapeLayer 无处可见。是什么阻止了 CAShapeLayer 出现在 LoadingView UIView 之上?

加载视图

class LoadingView: UIView {
    
//MARK: - Properties and Init
    
    let circleLayer = CAShapeLayer()
    private var circlePath : UIBezierPath = .init()
    let size : CGFloat = 80
    
    init() {
        super.init(frame: CGRect(x: 0, y: 0, width: size, height: size))
        tag = 100
        addShadow(shadowColor: UIColor.label.cgColor, shadowOffset: CGSize(width: 0, height: 0), shadowOpacity: 0.3, shadowRadius: 3)
        backgroundColor = .secondarySystemBackground
        self.layer.addSublayer(circleLayer)
        calculateCirclePath()
        animateCircle(duration: 1, repeats: true)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
//MARK: - Private UI Setup Methods
    
    private func calculateCirclePath() {
        self.circlePath = UIBezierPath(arcCenter: CGPoint(x: size / 2, y: size / 2), radius: size / 3, startAngle: 0.0, endAngle: CGFloat(Double.pi*2), clockwise: true)
    }
    
    override func layoutSubviews() {
        circleLayer.frame = self.layer.bounds
    }
    
    private func animateCircle(duration: TimeInterval, repeats: Bool) {
        // Setup the CAShapeLayer with the path, colors, and line width
        circleLayer.path = circlePath.cgPath
        circleLayer.fillColor = UIColor.clear.cgColor
        circleLayer.strokeColor = UIColor.blue.cgColor
        circleLayer.lineWidth = 5.0
        
        // Don't draw the circle initially
        circleLayer.strokeEnd = 0.0
        
        // Add the circleLayer to the view's layer's sublayers
        self.layer.addSublayer(circleLayer)
        // We want to animate the strokeEnd property of the circleLayer
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.duration = duration
        animation.repeatCount = repeats ? .infinity : 1
        animation.fromValue = 0
        animation.toValue = 1
        animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut)
        circleLayer.strokeEnd = 0
        circleLayer.add(animation, forKey: "animateCircle")
        
        let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
        rotationAnimation.repeatCount = repeats ? .infinity : 1
        rotationAnimation.fromValue = 0.0
        rotationAnimation.toValue = Double.pi*3
        rotationAnimation.duration = duration
        rotationAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
        circleLayer.add(rotationAnimation, forKey: nil)
        
        let fadeAnimation = CABasicAnimation(keyPath: "opacity")
        fadeAnimation.repeatCount = repeats ? .infinity : 1
        fadeAnimation.fromValue = 1
        fadeAnimation.toValue = 0
        fadeAnimation.duration = duration
        fadeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
        circleLayer.add(fadeAnimation, forKey: nil)
    }
}

ViewController 的扩展

extension ViewController {
    func startLoading() {
            view.addSubview(loadingView)
            loadingView.translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                loadingView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
                loadingView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
                loadingView.widthAnchor.constraint(equalToConstant: CGFloat(80)),
                loadingView.heightAnchor.constraint(equalToConstant: CGFloat(80)),
            ])
        loadingView.isHidden = false
    }
    
    func stopLoading() {
        loadingView.isHidden = true
    }
}

标签: iosswiftiphonexcodeuiview

解决方案


更好的做法是使用 dispatchQueue 在主线程中调用此方法,因为有时控制可能在后台线程中,因此必须在执行任何 UI 更新之前将其返回到主线程


推荐阅读