Saya ingin menampilkan tampilan pada UIViewController menggunakan UIView yang disebut LoadingView. Namun, setiap kali saya mencoba menggunakan kode berikut, hanya UIView yang muncul tanpa CALayer.

Idealnya, kode ini akan menyebabkan UIView muncul saat startLoading() dipanggil. CALayer kemudian akan ditambahkan ke UIView dan itu akan menjadi lingkaran yang berputar di sekitar pusat tampilan. Apa yang bisa saya ubah agar CALayer ini muncul dengan cara yang benar di UIView?

class LoadingView: UIView {
    
    let circleLayer = CAShapeLayer()

    init() {
        super.init(frame: .zero)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func startLoading() {
        let size = 50
        if let topView = UIApplication.topViewController()?.view {
            topView.addSubview(self)
            translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                self.centerYAnchor.constraint(equalTo: topView.centerYAnchor),
                self.centerXAnchor.constraint(equalTo: topView.centerXAnchor),
                self.widthAnchor.constraint(equalToConstant: CGFloat(size)),
                self.heightAnchor.constraint(equalToConstant: CGFloat(size)),
            ])
            self.layer.cornerRadius = frame.height/3
            addShadow(shadowColor: UIColor.label.cgColor, shadowOffset: CGSize(width: 0, height: 0), shadowOpacity: 0.3, shadowRadius: 2)
            backgroundColor = .secondarySystemBackground
            animateCircle(duration: .infinity)
        }
    }
    
    private func animateCircle(duration: TimeInterval) {
        let circlePath = UIBezierPath(arcCenter: CGPoint(x: self.frame.midX, y: self.frame.midY), radius: (frame.size.width), startAngle: 0.0, endAngle: CGFloat(Double.pi*2), clockwise: true)

        // Setup the CAShapeLayer with the path, colors, and line width
        circleLayer.path = circlePath.cgPath
        circleLayer.fillColor = UIColor.clear.cgColor
        circleLayer.strokeColor = Constants.Colors.mainBlue?.cgColor
        circleLayer.lineWidth = 1.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.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.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.fromValue = 1
        fadeAnimation.toValue = 0
        fadeAnimation.duration = duration
        fadeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn)
        circleLayer.add(fadeAnimation, forKey: nil)
    }
    
    func stopLoading() {
        self.removeFromSuperview()
    }
    
}

1
helloworld12345 13 Mei 2021, 03:31

2 jawaban

Jawaban Terbaik

Ada beberapa hal yang salah:

  1. frame/bounds dari CALayer harus disetel secara akurat. Perlu diingat bahwa ini bisa berubah dari penempatan awalnya. Misalnya, dalam pengujian pertama saya, layer percaya bahwa itu adalah persegi panjang 0x0.

  2. Jalur kurva harus diatur dengan benar (terkait dengan #1)

  3. Waktu .infinity Anda saat ini akan membuat animasi membutuhkan waktu yang tidak terbatas untuk diselesaikan. Saya pikir yang Anda inginkan adalah animasi yang membutuhkan waktu tertentu dan berulang tanpa batas.

Mungkin ada perubahan lain yang sekarang saya lupakan, tetapi ini akan membantu Anda memulai:


class LoadingView: UIView {
    
    let circleLayer = CAShapeLayer()
    
    private var circlePath : UIBezierPath = .init()
    let size : CGFloat = 50
    
    init() {
        super.init(frame: .zero)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func startLoading() {
        if let topView = UIApplication.topViewController()?.view {
            topView.addSubview(self)
            translatesAutoresizingMaskIntoConstraints = false
            NSLayoutConstraint.activate([
                self.centerYAnchor.constraint(equalTo: topView.centerYAnchor),
                self.centerXAnchor.constraint(equalTo: topView.centerXAnchor),
                self.widthAnchor.constraint(equalToConstant: CGFloat(size)),
                self.heightAnchor.constraint(equalToConstant: CGFloat(size)),
            ])
            self.layer.cornerRadius = frame.height/3
            backgroundColor = .secondarySystemBackground
            self.layer.addSublayer(circleLayer)
            calculateCirclePath()
            animateCircle(duration: 1, repeats: true)
        }
    }
    
    func calculateCirclePath() {
        self.circlePath = UIBezierPath(arcCenter: CGPoint(x: size / 2, y: size / 2), radius: size / 2, 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 = 1.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)
    }
    
    func stopLoading() {
        self.removeFromSuperview()
    }
}
1
jnpdx 13 Mei 2021, 01:26

Saya perhatikan bahwa Anda tidak menyetel frame dari circleLayer. Coba atur sebagai tampilan Anda.

0
irons163 13 Mei 2021, 01:06