Coder Social home page Coder Social logo

niuguangfei / swift-circleprogressloadinganimation Goto Github PK

View Code? Open in Web Editor NEW

This project forked from dongxiexidu/swift-circleprogressloadinganimation

0.0 1.0 0.0 645 KB

环形进度条图片加载动画swift-circleProgressLoadingAnimation

Swift 100.00%

swift-circleprogressloadinganimation's Introduction

效果

如图,这个动画的是如何做的呢?

分析:

  • 1.环形进度指示器,根据下载进度来更新它
  • 2.扩展环,向内向外扩展这个环,中间扩展的时候,去掉这个遮盖

一.环形进度指示器

1.自定义View继承UIView,命名为CircularLoaderView.swift,此View将用来保存动画的代码

2.创建CAShapeLayer

let circlePathLayer = CAShapeLayer()
let circleRadius: CGFloat = 20.0

3.初始化CAShapeLayer

// 两个初始化方法都调用configure方法
override init(frame: CGRect) {
    super.init(frame: frame)
    configure()
}

required init?(coder aDecoder: NSCoder) {
    super.init(coder : aDecoder)
    configure()
}


// 初始化代码来配置这个shape layer:
func configure(){
    circlePathLayer.frame = bounds
    circlePathLayer.lineWidth = 2.0
    circlePathLayer.fillColor = UIColor.clear.cgColor
    circlePathLayer.strokeColor = UIColor.red.cgColor
    layer.addSublayer(circlePathLayer)
    backgroundColor = .white
    progress = 0.0
}

4.设置环形进度条的矩形frame

// 小矩形的frame
func circleFrame() -> CGRect {
    
    var circleFrame = CGRect(x: 0, y: 0, width: 2*circleRadius, height: 2*circleRadius)
    let circlePathBounds = circlePathLayer.bounds
    circleFrame.origin.x = circlePathBounds.midX - circleFrame.midX
    circleFrame.origin.y = circlePathBounds.midY - circleFrame.midY
    return circleFrame
}

可以参考下图,理解这个circleFrame

Snip20160705_3.png

5.每次自定义的这个view的size改变时,你都需要重新计算circleFrame,所以要将它放在一个独立的方法,方便调用

// 通过一个矩形(正方形)绘制椭圆(圆形)路径
func circlePath() -> UIBezierPath {
    return UIBezierPath.init(ovalIn: circleFrame())
}

6.由于layers没有autoresizingMask这个属性,你需要在layoutSubviews方法中更新circlePathLayerrame来恰当地响应viewsize变化

override func layoutSubviews() {
    super.layoutSubviews()
    
    circlePathLayer.frame = bounds
    circlePathLayer.path = circlePath().cgPath
}

7.给CircularLoaderView.swift文件添加一个CGFloat类型属性,自定义的settergetter方法,setter方法验证输入值要在0到1之间,然后赋值给layerstrokeEnd属性。

var progress : CGFloat{
    get{
        return circlePathLayer.strokeEnd
    }
    
    set{
        if (newValue > 1) {
            circlePathLayer.strokeEnd = 1
        }else if(newValue < 0){
            circlePathLayer.strokeEnd = 0
        }else{
            circlePathLayer.strokeEnd = newValue
        }
    }
}

8.利用Kingfisher,在image下载回调方法中更新progress. 此处是自定义ImageView,在storyboard中拖个ImageView,设置为自定义的ImageView类型,在这个ImageView初始化的时候就会调用下面的代码

class CustomImageView: UIImageView {
    // 创建一个实例对象
    let progressIndicatorView = CircularLoaderView(frame: CGRect.zero)
    
    // 从xib中加载会走这个方法
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        
        addSubview(progressIndicatorView)
        progressIndicatorView.frame = bounds
        

        let url = URL.init(string: "https://koenig-media.raywenderlich.com/uploads/2015/02/mac-glasses.jpeg")
        
        self.kf.setImage(with: url, placeholder: nil, options: nil, progressBlock: { [weak self] (reseivdSize, expectedSize) in
            self?.progressIndicatorView.progress = CGFloat(reseivdSize) / CGFloat(expectedSize)
        }) { [weak self] (image, error, _, _) in
            self?.progressIndicatorView.reveal()
        }
    }
}

二.扩展这个环

仔细看,此处是两个动画一起执行,1是向外扩展2.是向内扩展.但可以用一个Bezier path完成此动画,需要用到组动画.

  • 1.增加圆的半径(path属性)来向外扩展
  • 2.同时增加line的宽度(lineWidth属性)来使环更加厚和向内扩展
func reveal() {
    // 背景透明,那么藏着后面的imageView将显示出来
    backgroundColor = .clear
    progress = 1.0
    
    // 移除隐式动画,否则干扰reveal animation
    circlePathLayer.removeAnimation(forKey: "strokenEnd")
    
    // 从它的superLayer 移除circlePathLayer ,然后赋值给super的layer mask
    circlePathLayer.removeFromSuperlayer()
    // 通过这个这个circlePathLayer 的mask hole动画 ,image 逐渐可见
    superview?.layer.mask = circlePathLayer
    
    // 1 求出最终形状
    let center = CGPoint(x:bounds.midX,y: bounds.midY)
    let finalRadius = sqrt((center.x*center.x) + (center.y*center.y))
    let radiusInset = finalRadius - circleRadius
    
    
    let outerRect = circleFrame().insetBy(dx: -radiusInset, dy: -radiusInset)
    // CAShapeLayer mask最终形状
    let toPath = UIBezierPath.init(ovalIn: outerRect).cgPath
    
    
    // 2 初始值
    let fromPath = circlePathLayer.path
    let fromLineWidth = circlePathLayer.lineWidth
    
    // 3 最终值
    CATransaction.begin()
    // 防止动画完成跳回原始值
    CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
    circlePathLayer.lineWidth = 2 * finalRadius
    circlePathLayer.path = toPath
    CATransaction.commit()
    
    // 4 路径动画,lineWidth动画
    let lineWidthAnimation = CABasicAnimation(keyPath: "lineWidth")
    lineWidthAnimation.fromValue = fromLineWidth
    lineWidthAnimation.toValue = 2*finalRadius
    let pathAnimation = CABasicAnimation(keyPath: "path")
    pathAnimation.fromValue = fromPath
    pathAnimation.toValue = toPath
    
    // 5 组动画
    let groupAnimation = CAAnimationGroup()
    groupAnimation.duration = 1
    groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
    groupAnimation.animations = [pathAnimation ,lineWidthAnimation]
    groupAnimation.delegate = self
    circlePathLayer.add(groupAnimation, forKey: "strokeWidth")
}

photo-loading-diagram.png

###三.监听动画的结束

extension CircularLoaderView :CAAnimationDelegate {
    // 移除mask
    func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
        superview?.layer.mask = nil
    }
}

简书:iOS-Swift环形进度指示器+图片加载动画 示例下载地址github 原文地址 Rounak Jain 参考地址

swift-circleprogressloadinganimation's People

Contributors

dongxiexidu avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.