于佳斌
于佳斌

Reputation: 11

How can I make a animation like ig story loading style in ios?

how to make a line dash animation like ig story?i try to make dynamic line dash ,but failed ;

(  CABasicAnimation *rotateAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
rotateAnimation.fromValue = @0;
rotateAnimation.toValue = @(M_PI_2*3);
CABasicAnimation * strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
strokeStartAnimation.fromValue = @(0);
strokeStartAnimation.toValue = @0.6;

CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
animationGroup.animations = @[rotateAnimation,strokeStartAnimation];
animationGroup.duration = 1.5;
animationGroup.fillMode = kCAFillModeForwards;
animationGroup.removedOnCompletion = NO;
animationGroup.delegate = self;
animationGroup.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[self.circlarShapeLayer addAnimation:animationGroup forKey:@"animationGroup"];

Upvotes: 0

Views: 1063

Answers (1)

agibson007
agibson007

Reputation: 4373

I would probably look into CAReplicatorLayer. You can do some interesting things with it. I took a quick stab in Playgrounds. You can play with the the timing and instance delay to see if you can get it to your liking. I also did not put a lot of thought into the instance color change. You could also set a gradient image to a layer and mask it with the CAShapeLayer and add that to the replicator. The only other way would be to add the layers manually which you could do using the same code that the CAReplicatorLayer uses in the below code and you would then have more control. You would just have a for loop and keep rotating the layer around or using a different portion of the stroke. Then keep and array of those and animate when needed.

import Foundation
import UIKit
import PlaygroundSupport

class InstagramProfileSpinner : UIView{

    var circlePiece : CAShapeLayer = CAShapeLayer()
    var replicator : CAReplicatorLayer = CAReplicatorLayer()
    lazy var imageView : UIImageView = {
        let img = UIImageView(frame: self.bounds)
        img.layer.cornerRadius = self.bounds.width/2
        img.layer.masksToBounds = true
        img.contentMode = .scaleAspectFill
        return img
    }()
    var avatarURL : URL?{
        didSet{
            configureAvatar()
        }
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        setUpShapeLayer()
        self.addSubview(imageView)
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
        setUpShapeLayer()
        self.addSubview(imageView)
    }

    func setUpShapeLayer(){

        if let sublayers = self.layer.sublayers,sublayers.contains(replicator){} else{

            circlePiece = CAShapeLayer()
            circlePiece.frame = self.bounds
            circlePiece.path = self.pathForCircle()
            circlePiece.strokeColor = UIColor(red: 254/255, green: 136/255, blue: 7/255, alpha: 1).cgColor
            circlePiece.lineWidth = 2.5
            circlePiece.lineJoin = .round
            circlePiece.lineCap = .round
            circlePiece.fillColor = UIColor.clear.cgColor
            circlePiece.strokeEnd = 0.025
            let count = 1/circlePiece.strokeEnd
            //set up replicator
            replicator.instanceCount = Int(count)
            let angle = (2.0*Double.pi)/Double(count)
            replicator.instanceTransform = CATransform3DMakeRotation(CGFloat(angle), 0.0, 0.0, 1.0)
            replicator.instanceRedOffset = -0.002
            replicator.instanceGreenOffset = -0.04
            replicator.instanceBlueOffset = -0.02
            replicator.addSublayer(circlePiece)
            self.layer.addSublayer(replicator)
        }
    }

    func pathForCircle()->CGPath{
        let path = UIBezierPath(ovalIn: self.bounds.insetBy(dx: 5, dy: 5))
        return path.cgPath
    }

    func animateSpinner(){
        let basic = CABasicAnimation(keyPath: "strokeEnd")
        basic.fromValue = circlePiece.strokeEnd
        basic.toValue = circlePiece.strokeEnd/5
        basic.duration = 1
        basic.autoreverses = true
        basic.repeatCount = .infinity
        replicator.instanceDelay = 0.25
        circlePiece.add(basic, forKey: "littleStrokes")
    }

    func removeAnimation(){
        if let animation = circlePiece.presentation(),
            let stroke = animation.value(forKeyPath: "strokeEnd") as? CGFloat{
            let final = CABasicAnimation(keyPath: "strokeEnd")
            final.toValue = circlePiece.strokeEnd
            final.duration = 1
            replicator.instanceDelay = 0
            circlePiece.add(final, forKey: "littleStrokes")
        }
    }
    func configureAvatar(){
        guard let url = self.avatarURL else{return}
        self.imageView.image = nil
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let dt = data{
                DispatchQueue.main.async {
                    self.imageView.image = UIImage(data: dt)
                }
            }
        }.resume()
    }
    override func layoutSubviews() {
        super.layoutSubviews()
        replicator.frame = self.bounds
        let inset = self.bounds.width * 0.08
        imageView.frame = self.bounds.insetBy(dx: inset, dy:inset)
        imageView.layer.cornerRadius = imageView.frame.width/2
    }
}

class ViewController:UIViewController{
    var check = true
    var circle = InstagramProfileSpinner(frame: CGRect(x: 30, y: 30, width: 100, height: 100))
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        view.addSubview(circle)
        let url = URL(string: "https://images.pexels.com/photos/450271/pexels-photo-450271.jpeg?auto=compress&cs=tinysrgb&h=750&w=1260")
        circle.avatarURL = url
        circle.animateSpinner()

        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 10) {
            self.circle.removeAnimation()
        }
    }
}


let viewController = ViewController()
PlaygroundPage.current.liveView = viewController
PlaygroundPage.current.needsIndefiniteExecution

Upvotes: 2

Related Questions