How do I apply the circle animation to the cell? - ios

I have a play button in each cell, and when it is clicked I want the circle animation will initiate and circle the circumference of the button. I have a separate swift file for the animation, but I do not know how to implement that to the cell. How would I do that? Below is the code for the button in the cell.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = table.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
cell.textLabel?.text = ret[indexPath.row]
cell.textLabel?.font = UIFont(name: "Lombok", size: 22)
cell.textLabel?.textColor = UIColorFromRGB("4A90E2")
let playButton : UIButton = UIButton(type: UIButtonType.Custom)
playButton.tag = indexPath.row
let imageret = "playbutton"
playButton.setImage(UIImage(named: imageret), forState: .Normal)
playButton.frame = CGRectMake(230, 20, 100, 100)
playButton.addTarget(self,action: "playit:", forControlEvents: UIControlEvents.TouchUpInside)
for view: UIView in cell.contentView.subviews {
view.removeFromSuperview()
}
cell.contentView.addSubview(playButton)
cell.backgroundColor = UIColor.clearColor()
return cell
}
func playit(sender: UIButton!){
}
Below is the code to the circle animation.
import UIKit
class Shape: UIView {
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
override func drawRect(rect: CGRect) {
// Drawing code
}
*/
var circleLayer: CAShapeLayer!
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clearColor()
// Use UIBezierPath as an easy way to create the CGPath for the layer.
// The path should be the entire circle.
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
// Setup the CAShapeLayer with the path, colors, and line width
circleLayer = CAShapeLayer()
circleLayer.path = circlePath.CGPath
circleLayer.fillColor = UIColor.clearColor().CGColor
circleLayer.strokeColor = UIColor.redColor().CGColor
circleLayer.lineWidth = 5.0;
// Don't draw the circle initially
circleLayer.strokeEnd = 0.0
// Add the circleLayer to the view's layer's sublayers
layer.addSublayer(circleLayer)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func animateCircle(duration: NSTimeInterval) {
// We want to animate the strokeEnd property of the circleLayer
let animation = CABasicAnimation(keyPath: "strokeEnd")
// Set the animation duration appropriately
animation.duration = duration
// Animate from 0 (no circle) to 1 (full circle)
animation.fromValue = 0
animation.toValue = 1
// Do a linear animation (i.e. the speed of the animation stays the same)
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
// Set the circleLayer's strokeEnd property to 1.0 now so that it's the
// right value when the animation ends.
circleLayer.strokeEnd = 1.0
// Do the actual animation
circleLayer.addAnimation(animation, forKey: "animateCircle")
}
func addCircleView() {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*50)
var circleWidth = CGFloat(200)
var circleHeight = circleWidth
// Create a new CircleView
var circleView = Shape(frame: CGRectMake(diceRoll, 0, circleWidth, circleHeight))
self.addSubview(circleView)
// Animate the drawing of the circle over the course of 1 second
circleView.animateCircle(1.0)
}
}

It looks like you're off to a good start. You need to rewrite your cellForRowAtIndexPath method so it only adds the button if you are creating a new cell (if dequeueReusableCellWithIdentifier returns nil.)
Then you need to add code in your button's IBAction (`playIt') that invokes your animation.
However, your animation code can't use a global circleLayer property. You need a way to fetch the shape layer from the button. You could create a custom subclass of UIButton that has a shape layer property, or you could loop through sender's sublayers in the IBAction, looking for a shape layer.

Related

ios animation - display image/fill color from left to right with animating

I have a image, been added to CALayer.contents.
The image basically contains a word/letters, the requirement is to animate like showing up letters from left to right gradually, mean while animating the image positions as like initially 1st letter on the middle and eventually whole image been at central position.
Any suggestion on how to achieve this with Core animation? Code snippets will be greatly appreciated.
One thought I had is to set the image colour same as background colour at initial state(looks transparent), and fill image colour to desired colour from left to right with animation. And at the same time animate the image position.
You can do this by using a gradient layer as a mask, then animating the position of the image layer and the locations of the gradient.
Here's a quick example to get started:
class RevealView: UIView {
public var image: UIImage? {
didSet {
imgLayer.contents = image?.cgImage
}
}
public var duration: TimeInterval = 1.0
private let gradLayer = CAGradientLayer()
private let imgLayer = CALayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
clipsToBounds = true
layer.addSublayer(imgLayer)
imgLayer.contentsGravity = .resize
// white area shows through, clear area is "hidden"
gradLayer.colors = [UIColor.white.cgColor, UIColor.clear.cgColor]
// left-to-right gradient
gradLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
// start locations so entire layer is masked
gradLayer.locations = [0.0, 0.0]
// set the mask
layer.mask = gradLayer
}
override func layoutSubviews() {
super.layoutSubviews()
// set image layer frame to view bounds
imgLayer.frame = bounds
// move it half-way to the right
imgLayer.position.x = bounds.maxX
// set gradient layer frame to view bounds
gradLayer.frame = bounds
}
public func doAnim() {
let imgAnim = CABasicAnimation(keyPath: "position.x")
let gradAnim = CABasicAnimation(keyPath: "locations")
// animate image layer from right-to-left
imgAnim.toValue = bounds.midX
// animate gradient from left-to-right
gradAnim.toValue = [NSNumber(value: 1.0), NSNumber(value: 2.0)]
imgAnim.duration = self.duration
gradAnim.duration = imgAnim.duration * 2.0
[imgAnim, gradAnim].forEach { anim in
anim.isRemovedOnCompletion = false
anim.fillMode = .forwards
anim.beginTime = CACurrentMediaTime()
}
CATransaction.begin()
CATransaction.setDisableActions(true)
imgLayer.add(imgAnim, forKey: nil)
gradLayer.add(gradAnim, forKey: nil)
CATransaction.commit()
}
}
class RevealVC: UIViewController {
let testView = RevealView()
override func viewDidLoad() {
super.viewDidLoad()
guard let img = UIImage(named: "mt") else {
fatalError("Could not load image!")
}
testView.image = img
// use longer or shorter animation duration if desired (default is 1.0)
//testView.duration = 3.0
testView.translatesAutoresizingMaskIntoConstraints = false
testView.backgroundColor = .clear
view.addSubview(testView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
testView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
testView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
testView.widthAnchor.constraint(equalToConstant: img.size.width),
testView.heightAnchor.constraint(equalToConstant: img.size.height),
])
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
testView.doAnim()
}
}

Animate UIView's Layer with constrains (Auto Layout Animations)

I am working on project where I need to animate height of view which consist of shadow, gradient and rounded corners (not all corners).
So I have used layerClass property of view.
Below is simple example demonstration.
Problem is that, when I change height of view by modifying its constraint, it was resulting in immediate animation of layer class, which is kind of awkward.
Below is my sample code
import UIKit
class CustomView: UIView{
var isAnimating: Bool = false
override init(frame: CGRect) {
super.init(frame: frame)
self.setupView()
}
func setupView(){
self.layoutMargins = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
guard let layer = self.layer as? CAShapeLayer else { return }
layer.fillColor = UIColor.green.cgColor
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0.0, height: 2.0)
layer.shadowOpacity = 0.6
layer.shadowRadius = 5
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override class var layerClass: AnyClass {
return CAShapeLayer.self
}
override func layoutSubviews() {
super.layoutSubviews()
// While animating `myView` height, this method gets called
// So new bounds for layer will be calculated and set immediately
// This result in not proper animation
// check by making below condition always true
if !self.isAnimating{ //if true{
guard let layer = self.layer as? CAShapeLayer else { return }
layer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
layer.shadowPath = layer.path
}
}
}
class TestViewController : UIViewController {
// height constraint for animating height
var heightConstarint: NSLayoutConstraint?
var heightToAnimate: CGFloat = 200
lazy var myView: CustomView = {
let view = CustomView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .clear
return view
}()
lazy var mySubview: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .yellow
return view
}()
lazy var button: UIButton = {
let button = UIButton(frame: .zero)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Animate", for: .normal)
button.setTitleColor(.black, for: .normal)
button.addTarget(self, action: #selector(self.animateView(_:)), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .white
self.view.addSubview(self.myView)
self.myView.addSubview(self.mySubview)
self.view.addSubview(self.button)
self.myView.leadingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.leadingAnchor).isActive = true
self.myView.trailingAnchor.constraint(equalTo: self.view.layoutMarginsGuide.trailingAnchor).isActive = true
self.myView.topAnchor.constraint(equalTo: self.view.layoutMarginsGuide.topAnchor, constant: 100).isActive = true
self.heightConstarint = self.myView.heightAnchor.constraint(equalToConstant: 100)
self.heightConstarint?.isActive = true
self.mySubview.leadingAnchor.constraint(equalTo: self.myView.layoutMarginsGuide.leadingAnchor).isActive = true
self.mySubview.trailingAnchor.constraint(equalTo: self.myView.layoutMarginsGuide.trailingAnchor).isActive = true
self.mySubview.topAnchor.constraint(equalTo: self.myView.layoutMarginsGuide.topAnchor).isActive = true
self.mySubview.bottomAnchor.constraint(equalTo: self.myView.layoutMarginsGuide.bottomAnchor).isActive = true
self.button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.button.bottomAnchor.constraint(equalTo: self.view.layoutMarginsGuide.bottomAnchor, constant: -20).isActive = true
}
#objc func animateView(_ sender: UIButton){
CATransaction.begin()
CATransaction.setAnimationDuration(5.0)
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut))
UIView.animate(withDuration: 5.0, animations: {
self.myView.isAnimating = true
self.heightConstarint?.constant = self.heightToAnimate
// this will call `myView.layoutSubviews()`
// and layer's new bound will set automatically
// this causing layer to be directly jump to height 200, instead of smooth animation
self.view.layoutIfNeeded()
}) { (success) in
self.myView.isAnimating = false
}
let shadowPathAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.shadowPath))
let pathAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.path))
let toValue = UIBezierPath(
roundedRect:CGRect(x: 0, y: 0, width: self.myView.bounds.width, height: heightToAnimate),
cornerRadius: 10
).cgPath
shadowPathAnimation.fromValue = self.myView.layer.shadowPath
shadowPathAnimation.toValue = toValue
pathAnimation.fromValue = (self.myView.layer as! CAShapeLayer).path
pathAnimation.toValue = toValue
self.myView.layer.shadowPath = toValue
(self.myView.layer as! CAShapeLayer).path = toValue
self.myView.layer.add(shadowPathAnimation, forKey: #keyPath(CAShapeLayer.shadowPath))
self.myView.layer.add(pathAnimation, forKey: #keyPath(CAShapeLayer.path))
CATransaction.commit()
}
}
While animating view, it will call its layoutSubviews() method, which will result into recalculating bounds of shadow layer.
So I checked if view is currently animating, then do not recalculate bounds of shadow layer.
Is this approach right ? or there is any better way to do this ?
I know it's a tricky question. Actually, you don't need to care about layoutSubViews at all. The key here is when you set the shapeLayer. If it's setup well, i.e. after the constraints are all working, you don't need to care that during the animation.
//in CustomView, comment out the layoutSubViews() and add updateLayer()
func updateLayer(){
guard let layer = self.layer as? CAShapeLayer else { return }
layer.path = UIBezierPath(roundedRect: layer.bounds, cornerRadius: 10).cgPath
layer.shadowPath = layer.path
}
// override func layoutSubviews() {
// super.layoutSubviews()
//
// // While animating `myView` height, this method gets called
// // So new bounds for layer will be calculated and set immediately
// // This result in not proper animation
//
// // check by making below condition always true
//
// if !self.isAnimating{ //if true{
// guard let layer = self.layer as? CAShapeLayer else { return }
//
// layer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
// layer.shadowPath = layer.path
// }
// }
in ViewController: add viewDidAppear() and remove other is animation block
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
myView.updateLayer()
}
#objc func animateView(_ sender: UIButton){
CATransaction.begin()
CATransaction.setAnimationDuration(5.0)
CATransaction.setAnimationTimingFunction(CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut))
UIView.animate(withDuration: 5.0, animations: {
self.heightConstarint?.constant = self.heightToAnimate
// this will call `myView.layoutSubviews()`
// and layer's new bound will set automatically
// this causing layer to be directly jump to height 200, instead of smooth animation
self.view.layoutIfNeeded()
}) { (success) in
self.myView.isAnimating = false
}
....
Then you are good to go. Have a wonderful day.
Below code also worked for me, As I want to use layout subviews without any flags.
UIView.animate(withDuration: 5.0, animations: {
let shadowPathAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.shadowPath))
let pathAnimation = CABasicAnimation(keyPath: #keyPath(CAShapeLayer.path))
let toValue = UIBezierPath(
roundedRect:CGRect(x: 0, y: 0, width: self.myView.bounds.width, height: self.heightToAnimate),
cornerRadius: 10
).cgPath
shadowPathAnimation.fromValue = self.myView.layer.shadowPath
shadowPathAnimation.toValue = toValue
pathAnimation.fromValue = (self.myView.layer as! CAShapeLayer).path
pathAnimation.toValue = toValue
self.heightConstarint?.constant = self.heightToAnimate
self.myView.layoutIfNeeded()
self.myView.layer.shadowPath = toValue
(self.myView.layer as! CAShapeLayer).path = toValue
self.myView.layer.add(shadowPathAnimation, forKey: #keyPath(CAShapeLayer.shadowPath))
self.myView.layer.add(pathAnimation, forKey: #keyPath(CAShapeLayer.path))
CATransaction.commit()
})
And overriding layoutSubview as follows
override func layoutSubviews() {
super.layoutSubviews()
guard let layer = self.layer as? CAShapeLayer else { return }
layer.path = UIBezierPath(roundedRect: bounds, cornerRadius: 10).cgPath
layer.shadowPath = layer.path
}

Animating CAShapeLayer according to button (using auto layout)

My button is being resized by using auto layout. The button has a CAShapeLayer which it's frame is being set at layoutSubviews:
var initial = true
override func layoutSubviews() {
super.layoutSubviews()
if initial {
border.frame = layer.bounds
border.path = UIBezierPath(rect: bounds).CGPath
initial = false
}
}
Because of this, whenever the size of this button is changed, the layer is changed immediately without any animation (which hides the button animation itself).
I've tried to add an animation:
override func layoutSubviews() {
super.layoutSubviews()
func movePosition() {
let fromValue = border.position.x
let toValue = center.x
CATransaction.setDisableActions(true)
border.position.x = toValue
let positionAnimation = CABasicAnimation(keyPath: "position.x")
positionAnimation.fromValue = fromValue
positionAnimation.toValue = toValue
positionAnimation.duration = 0.5
border.addAnimation(positionAnimation, forKey: "position")
}
func changesWidth() {
let fromValue = border.frame.size.width
let toValue = layer.bounds.width
CATransaction.setDisableActions(true)
border.frame.size.width = toValue
let widthAnimation = CABasicAnimation(keyPath: "frame.size.width")
widthAnimation.fromValue = fromValue
widthAnimation.toValue = toValue
widthAnimation.duration = 0.5
border.addAnimation(widthAnimation, forKey: "width")
}
if initial {
border.frame = layer.bounds
border.path = UIBezierPath(rect: bounds).CGPath
initial = false
} else {
movePosition()
changesWidth()
}
}
But with this, the animation is totally messed up (and layoutSubview is called a few times initially so it messes the layers up at the start.
Is it right to be trying to animate the layer in layoutSubviews at all? Or should I calculate the widths manually and animate the layers from there?

Having custom CAShapeLayer animate with the Button

I'm animating my button by changing a constraint of my auto layout and using an UIView animation block to animate it:
UIView.animateWithDuration(0.5, animations: { self.layoutIfNeeded() })
In this animation, only the width of the button is changing and the button itself is animating.
In my button, there's a custom CAShapeLayer. Is it possible to catch the animation of the button and add it to the layer so it animates together with the button?
What I've Tried:
// In my CustomButton class
override func actionForLayer(layer: CALayer, forKey event: String) -> CAAction? {
if event == "bounds" {
if let action = super.actionForLayer(layer, forKey: "bounds") as? CABasicAnimation {
let animation = CABasicAnimation(keyPath: event)
animation.fromValue = border.path
animation.toValue = UIBezierPath(rect: bounds).CGPath
// Copy values from existing action
border.addAnimation(animation, forKey: nil) // border is my CAShapeLayer
}
return super.actionForLayer(layer, forKey: event)
}
// In my CustomButton class
override func layoutSubviews() {
super.layoutSubviews()
border.frame = layer.bounds
let fromValue = border.path
let toValue = UIBezierPath(rect: bounds).CGPath
CATransaction.setDisableActions(true)
border.path = toValue
let animation = CABasicAnimation(keyPath: "path")
animation.fromValue = fromValue
animation.toValue = toValue
animation.duration = 0.5
border.addAnimation(animation, forKey: "animation")
}
Nothing is working, and I've been struggling for days..
CustomButton:
class CustomButton: UIButton {
let border = CAShapeLayer()
init() {
super.init(frame: CGRectZero)
translatesAutoresizingMaskIntoConstraints = false
border.fillColor = UIColor.redColor().CGColor
layer.insertSublayer(border, atIndex: 0)
}
// override func layoutSubviews() {
// super.layoutSubviews()
//
// border.frame = layer.bounds
// border.path = UIBezierPath(rect: bounds).CGPath
// }
override func layoutSublayersOfLayer(layer: CALayer) {
super.layoutSublayersOfLayer(layer)
border.frame = self.bounds
border.path = UIBezierPath(rect: bounds).CGPath
}
}
All you have to do is resize also the sublayers when the backing layer of your view is resized. Because of implicit animation the change should be animated. So all you need to do is basically to set this in you custom view class:
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
border.frame = self.bounds
}
Updated
I had some time to play with the animation and it seems to work for me now. This is how it looks like:
class TestView: UIView {
let border = CAShapeLayer()
init() {
super.init(frame: CGRectZero)
translatesAutoresizingMaskIntoConstraints = false
border.backgroundColor = UIColor.redColor().CGColor
layer.insertSublayer(border, atIndex: 0)
border.frame = CGRect(x: 0, y:0, width: 60, height: 60)
backgroundColor = UIColor.greenColor()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSublayersOfLayer(layer: CALayer) {
super.layoutSublayersOfLayer(layer)
CATransaction.begin();
CATransaction.setAnimationDuration(10.0);
border.frame.size.width = self.bounds.size.width
CATransaction.commit();
}
}
And I use it like this:
var tview: TestView? = nil
override func viewDidLoad() {
super.viewDidLoad()
tview = TestView();
tview!.frame = CGRect(x: 100, y: 100, width: 60, height: 60)
view.addSubview(tview!)
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tview!.frame.size.width = 200
}
The issue is that the frame property of CALayer is not animable. The docs say:
Note:Note
The frame property is not directly animatable. Instead you should animate the appropriate combination of the bounds, anchorPoint and position properties to achieve the desired result.
If you didn't solve your problem yet or for others that might have it I hope this helps.

UIView frame center in IBOutlet initialization is incorrect

I have a progress bar that's supposed to be rendered at the center of a view, but ends up off-center because of Auto Layout.
class ProgressView: UIView {
private let progressLayer: CAShapeLayer = CAShapeLayer()
private var progressLabel: UILabel
required init?(coder aDecoder: NSCoder) {
progressLabel = UILabel()
super.init(coder: aDecoder)
createProgressLayer()
createLabel()
}
override init(frame: CGRect) {
progressLabel = UILabel()
super.init(frame: frame)
createProgressLayer()
createLabel()
}
private func createProgressLayer() {
let startAngle = CGFloat(3*M_PI_2)
let endAngle = CGFloat(M_PI * 2 + 3*M_PI_2)
let centerPoint = CGPointMake(CGRectGetWidth(bounds)/2, CGRectGetHeight(bounds)/2)
let gradientMaskLayer = gradientMask()
progressLayer.path = UIBezierPath(arcCenter:centerPoint, radius: CGRectGetWidth(frame)/2-8, startAngle:startAngle, endAngle:endAngle, clockwise: true).CGPath
progressLayer.backgroundColor = UIColor.clearColor().CGColor
progressLayer.fillColor = nil
progressLayer.strokeColor = UIColor.blackColor().CGColor
progressLayer.lineWidth = 4.0
progressLayer.strokeStart = 0.0
progressLayer.strokeEnd = 0.0
gradientMaskLayer.mask = progressLayer
layer.addSublayer(gradientMaskLayer)
}
func animateProgressView() {
progressLayer.strokeEnd = 0.0
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = CGFloat(0.0)
animation.toValue = CGFloat(1.0)
animation.duration = 2.0
animation.delegate = self
animation.removedOnCompletion = false
animation.additive = true
animation.fillMode = kCAFillModeForwards
progressLayer.addAnimation(animation, forKey: "strokeEnd")
}
}
This code is called as follows:
class MainPageController: UIViewController {
#IBOutlet var progressView : ProgressView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
progressView.animateProgressView()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
And ends up drawing the circle off-center. If I manually edit the circle center, the right and bottom bars seem to be cut-off. If I turn auto-layout off, it works fine. What am I doing wrong?
Storyboard:
Thanks for posting the constrains from the storyboard, I can see whats causing it now.
ProgessView is setup with two constraints, one to keep it square and another to make its height 1/3 of the views height. This means the the size of the view that you see in the storyboard is not going the same as one the device.
init?(coder aDecoder: NSCoder) is called when loading a view from a storyboard. It's called before Auto Layout runs so the values you get for bounds and frame are based on the size in the storyboard which is 200x200. Then Auto Layout runs and determines the size for your phone (which looks like an iPhone 6) should be 222x222. Based on your screenshot, it looks like you have roughly 22pt of extra space on the right and bottom of your progress view.
What you'll want to do is resize your progress layer after Auto Layout sets your view to the proper size. The best place to do that would be in layoutSubviews by moving some line from createProgressLayer
func layoutSubviews() {
let startAngle = CGFloat(3*M_PI_2)
let endAngle = CGFloat(M_PI * 2 + 3*M_PI_2)
let centerPoint = CGPointMake(bounds.width/2, bounds.height/2)
progressLayer.path = UIBezierPath(arcCenter:centerPoint, radius: frame.width/2-8, startAngle:startAngle, endAngle:endAngle, clockwise: true).CGPath
}
In Interface Builder change the content mode of Progress View to redraw, and override drawRect and call createProgressLayer from there.

Resources