Kodeco Forums

Video Tutorial: CALayers Part 3: CAShapeLayer

Learn how to use CAShapeLayer to draw and stroke arbitrary paths thus creating cool effects in your apps.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/3246-calayers/lessons/4

HI Caroline ,

I have been using this tutorial actually one of the finished challenged projects provided. I was wondering if you have ever cancelling the CA Layer animation in progress. I crash when I try to reset things mid-flight. Other than that it is running great. I use it to show time remaining a video clips. It shows the person how long for instance to keep their arms up in the air for stretch. Occasionally the video stops faster than the CAAnimation and i try to restart it before completing. I have seen videos referencing the coordination of the AVPlayer and CALayers but I don’t want to go there yet.
Thanks much

Hi Again,

My late my panic has subsided and I re-looked at your code and derived this to do the job.
Thanks again for your video !

func stopAnimation() {
    CATransaction.begin()
    fgLayer.removeAnimationForKey("stroke")
    CATransaction.commit()
}

Thanks for letting me know you’ve fixed it :slight_smile: and sorry for not getting back to you earlier. I haven’t seen it crash, so was mulling it over.

Hey, caroline first of all you guys have awesome clarity in concept.
now to the Q.
I know we are supposed to create the projects from the starter one. but i went ahead and created my own from the scratch also instead of putting layouts programmatically i used labels (so without the programmatic constraints ) , but then my CAShapeLayer is not showing in the running app (but it shows in the storyboard (using @IBDesignable) )

import UIKit

@IBDesignable

class StatView: UIView {
    
     let margin : CGFloat = 10
    
     let bgLayer : CAShapeLayer = CAShapeLayer()
   
     let fgLayer : CAShapeLayer = CAShapeLayer()
    
    @IBInspectable var bgColor : UIColor = UIColor.clearColor()
        {
        didSet{
        configure()
        }
    }
    
    @IBInspectable var fgColor : UIColor = UIColor.clearColor(){
        didSet{
        configure()
        }
    }
    
    
  
    override func awakeFromNib()
    {
        super.awakeFromNib()
     print("Call in awakeFromNib recieved!")
        setup()
        configure()
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        setup()
        configure()
    
    }
    
func setup()
{
    bgLayer.lineWidth = 10
    bgLayer.fillColor = nil
    bgLayer.strokeEnd = 1
    fgLayer.lineWidth = 10
    fgLayer.fillColor = nil
    fgLayer.strokeEnd = 0.5
    layer.addSublayer(bgLayer)
    layer.addSublayer(fgLayer)
    
    
}
    
func configure()
   {
    bgLayer.strokeColor = bgColor.CGColor
    fgLayer.strokeColor = fgColor.CGColor
     }
    
func degreesToRadian(numberToBeConvertedIntoDegrees : Double) -> CGFloat
{
        let num  = (numberToBeConvertedIntoDegrees * M_PI) / 180
        return CGFloat(num)
    
    }
    
 private func setupShapeLayer(shapeLayer : CAShapeLayer)
  {
     shapeLayer.frame = self.bounds
    
     let startAngle = degreesToRadian(135)
     let endAngle = degreesToRadian(45)
     let center = self.center
     let radius = min(self.bounds.width , self.bounds.height) * 0.35
     let path : UIBezierPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: true)
        shapeLayer.path = path.CGPath
    }
    
    
    override func layoutSubviews() {
        super.layoutSubviews()
       print("layoutaubviews called!")
        bgLayer.frame = self.bounds
        fgLayer.frame = self.bounds
        setupShapeLayer(bgLayer)
        setupShapeLayer(fgLayer)
    }
    
}

this is my code for statView

Thanks in advance!

@darkknight - I can’t tell what’s wrong from that. Are you able to zip up the project and post it?

sorry to bother you…i figured it out! :-))

1 Like

Hi @caroline!

There is something that I don’t understand?

fgLayer.removeAnimationForKey(“stroke”)
fgLayer.addAnimation(animation, forKey: “stroke”)

why remove/add stroke and not strokeEnd instead?

Thanks!

@andreskwan - “strokeEnd” is the name of the actual property that you want to animate.

“stroke” is a name I made up.

If you put an “x” in front of “strokeEnd” then the property doesn’t exist and no animation will take place. I thought it would crash, but it just ignores it.

If you change “xstrokeEnd” back to “strokeEnd” and put an “x” in front of both "stroke"s it will work. Because this is a made-up name.

1 Like

Hi @caroline!
Now I understand that forKey: stroke is the name that we made up for the animation we are adding to the render tree, could be anything! I though that it should be the name of the property we are animating.

Thanks so much!

why hasn’t subtitle in this video :frowning: ?

Hi Caroline,

thanks for the tutorial, luckily it does not require a lot of tweaks to update it to Swift 3.
I feel that Layer Animations really deserve their own part and that introducing the concept in the challenge document only is a corner cut too much :slight_smile: The challenge is not really a challenge, more a tutorial. Not that this is a bad thing but I think it would be more clear if you dedicated it a separate episode.

Tomasz

@marszalcns - thanks for your feedback :slight_smile:

Agreed. I already have a strong understanding of the subject of CALayer basics, but lack the same knowledge of CALayer animations. I watched the videos specifically for the animation portion and was disappointed to see it as a challenge.

When (If) this series is updated for the latest version of Swift, there should be another video added to cover this concept. In the meantime, here is a tutorial on the topic.

For the challenge, is there a disadvantage to simply calling:

     fgLayer.strokeEnd = curValue/range

inside of the didSet property observer of curValue? It seems like this gives you the animation effect that we’re looking for in the challenge with just a single line of code. I assume that the challenge solution is the way that will allow for more customization, but is the above solution considered “hacky”?

@caroline Can you please help with this when you get a chance? Thank you - much appreciated! :]

@iosdevelopr - you’re right. I think from memory that it may have been for added understanding. (But not 100% sure.) There may be an update in the works :zipper_mouth_face:, and that will go through some unnecessary code for explanation and then reduce to the final didSet.

I’ll be on the lookout for that video if/when it happens! I always enjoy your videos. Thank you!

1 Like