• Open Xcode and create a new Project, choose the template Single View Application, enter the ProjectName( ImageLoaderExample) and click Next.
    • Now add an UIImageView to viewcontroller through Storyboard.
    • Create a file CustomImageDisplayView with subclass UIImageView.
    • Now go to storyboard and  click ImageView, In attribute Inspector choose custom class and assign it - CustomImageDisplayView name.
    • Add ImageIO framework and SDWebImage files in the project.
    • Open CustomImageDisplayView file and add following lines:
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let url = NSURL(string: "")
        sd_setImageWithURL(url, placeholderImage: nil, options: .CacheMemoryOnly, progress: {
          [weak self]
          (receivedSize, expectedSize) -> Void in
            //Update progress here 
          }) {
            [weak self]
            (image, error, _, _) -> Void in
            //Reveal image here
    • Now create a file (DisplayingCircularLoaderView) with subclass of UIView and  add following code:
        let circularPathLayer = CAShapeLayer()
        let circleRadius: CGFloat = 20.0
        override init(frame: CGRect) {
            super.init(frame: frame)
        required init(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)!
        func configure() {
            progress = 0
            circularPathLayer.frame = bounds
            circularPathLayer.lineWidth = 3
            circularPathLayer.fillColor = UIColor.clearColor().CGColor
            circularPathLayer.strokeColor = UIColor.blueColor().CGColor
            circularPathLayer.lineCap = kCALineCapRound;
            backgroundColor = UIColor.whiteColor()

    Section A - circularPathLayer is the circularPath and cirleRadius defines the radius of loader which will be shown.

    Section B -  Both initializers are having configure(). configure() sets up the line width of 3 , clear fill color , gives blue stroke color and lineCap adds rounded progress line. Then it add shape layer as a sublayer to the main view and make the background white so that when loader is running the background is blanked out.

    • Now add following line of code in DisplayingCircularLoaderView:
        func circularFrame() -> CGRect {
            var circularFrame = CGRect(x: 0, y: 0, width: 2*circleRadius, height: 2*circleRadius)
            circularFrame.origin.x = CGRectGetMidX(circularPathLayer.bounds) - CGRectGetMidX(circularFrame)
            circularFrame.origin.y = CGRectGetMidY(circularPathLayer.bounds) - CGRectGetMidY(circularFrame)
            return circularFrame
        func circularPath() -> UIBezierPath {
            return UIBezierPath(ovalInRect: circularFrame())
        override func layoutSubviews() {
            circularPathLayer.frame = bounds
            circularPathLayer.path = circularPath().CGPath

    Section C - The circularFrame() returns the CGRect which bounds the frame of  indicator's path.  Width and height of circularFrame is twice the circleRadius and lies in center of the view.

    Section D - This returns UIBezierPath which is bounded by the circularFrame () 

    Section E - The layoutSubviews() update the circlePathLayers frame as the view size changes.

    • Now open CustomImageDisplayView and add following codes:
       let progressIndicatorView = DisplayingCircularLoaderView(frame: CGRectZero)
    • Add these lines in init(coder: ) before the url is defined:
        progressIndicatorView.frame = bounds
        progressIndicatorView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight] 

    This will add progressIndicator view as a subview to the customImageview

    • Now add following code in DisplayingCircularLoaderView which has custom getter and setter, the getter returns circlePathLayer.strokeEnd and setter changes between o and 1 and sets the strokeEnd accordingly.
       var progress: CGFloat {
            get {
                return circularPathLayer.strokeEnd
            set {
                if (newValue > 1) {
                    circularPathLayer.strokeEnd = 1
                } else if (newValue < 0) {
                    circularPathLayer.strokeEnd = 0
                } else {
                    circularPathLayer.strokeEnd = newValue
    • Open CustomImageDisplayView and replace the comment Update progress here with:
         self!.progressIndicatorView.progress = CGFloat(receivedSize)/CGFloat(expectedSize)

    This calculates the size of image downloaded and accordingly the progress indicator view is managed.

    • Add following code in DisplayingCircularLoaderView :
      func reveal() {
            backgroundColor = UIColor.clearColor()
            progress = 1
            superview?.layer.mask = circularPathLayer

    This method clears the view's background so that image behind the view is not remain hidden, removes the implicit animation for strokeEnd and remove circularPathLayer from its layer.

    • Now add this line in the CustomImageDisplayView :
    • Open reveal() in DisplayingCircularLoaderView and add following lines of code to it:
            let center = CGPoint(x: CGRectGetMidX(bounds), y: CGRectGetMidY(bounds))
            let finalRadius = sqrt((center.x*center.x) + (center.y*center.y))
            let radiusInset = finalRadius - circleRadius
            let outerRect = CGRectInset(circularFrame(), -radiusInset, -radiusInset)
            let toPath = UIBezierPath(ovalInRect: outerRect).CGPath
            let fromPath = circularPathLayer.path
            let fromLineWidth = circularPathLayer.lineWidth
            CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions)
            circularPathLayer.lineWidth = 2*finalRadius
            circularPathLayer.path = toPath
            let lineWidthBasicAnimation = CABasicAnimation(keyPath: "lineWidth")
            lineWidthBasicAnimation.fromValue = fromLineWidth
            lineWidthBasicAnimation.toValue = 2*finalRadius
            let pathBasicAnimation = CABasicAnimation(keyPath: "path")
            pathBasicAnimation.fromValue = fromPath
            pathBasicAnimation.toValue = toPath
            let groupAnimation = CAAnimationGroup()
            groupAnimation.duration = 1
            groupAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
            groupAnimation.animations = [pathBasicAnimation, lineWidthBasicAnimation]
            groupAnimation.delegate = self
            circularPathLayer.addAnimation(groupAnimation, forKey: "strokeWidth") 

    Section H - It determine the radius of circle and the calculate the area of view in which that circle would fully bound .

    Section I - lineWidth and path initial values are set for match values of the layer.

    Section J - Hhen animation completes lineWidth and path final values are set. kCATransactionDisableActions are set to true for disabling layers animations. 

    Section K - Two instances are created for path and lineWidth. lineWidth should increase with double rate as the radius increases so that it can expand inward as well as outward. Now both instances are added to animation group to layer. 

    • Now at last add this method to DisplayingCircularLoaderView :
        override func animationDidStop(anim: CAAnimation!, finished flag: Bool) {
            superview?.layer.mask = nil

    This will remove the circle completely from the view



Tags: OpenXCode

Mobile Applications

Video Content

Bigdata & NoSQL

SaaS Applications



Alexa Certified Site Stats for