1

I have my own custom view that acts like a progress bar. Here is the code for it:

class ProgressBar: UIView {

    var bottomProgressBar = CAShapeLayer()
    var topProgressBar = CAShapeLayer()

    override init(frame: CGRect) {
        super.init(frame: frame)
        configure()
    }

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

    func configure() {
        bottomProgressBar.strokeColor = UIColor.white.cgColor
        bottomProgressBar.opacity = 0.1
        bottomProgressBar.lineWidth = self.frame.height
        bottomProgressBar.strokeStart = 0
        bottomProgressBar.strokeEnd = 1
        self.layer.addSublayer(bottomProgressBar)

        topProgressBar.strokeColor = UIColor.white.cgColor
        topProgressBar.lineWidth = self.frame.height
        topProgressBar.strokeStart = 0
        topProgressBar.strokeEnd = 1
        self.layer.addSublayer(topProgressBar)

        animate(toValue: 0)
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let path = UIBezierPath()
        path.move(to: CGPoint(x: 0, y: 0))
        path.addLine(to: CGPoint(x: self.frame.width, y: 0))

        bottomProgressBar.path = path.cgPath
        topProgressBar.path = path.cgPath
    }
}

Now, when I add this using storyboards, by adding an UIView inside the controller and setting the Custom Class to ProgressBar I can see it visible when running the app (it should have a white color with alpha 0.1).

But when I'm trying to added programatically , it's not visible at all. Only if I explicitly set the background color:

myProgressBar.backgroudColor = .red

This is not what I want. I want it to have the white color and alpha 0.1 that is the initial value.

Here is my code for adding it programatically:

var progressBar = ProgressBar()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(progressBar)

        progressBar.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint(item: progressBar, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: progressBar, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true
        NSLayoutConstraint(item: progressBar, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true
        NSLayoutConstraint(item: progressBar, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 1).isActive = true
    }

I don't seem to understand what is the difference between adding it using storyboards and adding it programatically.
I see that the constraints are correct and the view is added, but why doesn't the default color appear ?

Does anyone have an idea what my issue is in this case ?

edit:

Added code for the animate method:

func animate(toValue: Double) {
    let animation = CABasicAnimation(keyPath: "strokeEnd")

    animation.duration = 1.0
    animation.fromValue = 0
    animation.toValue = toValue

    topProgressBar.strokeEnd = CGFloat(toValue)
    topProgressBar.animation(forKey: "animate")
}
2
  • I'd recommend double-checking the constraints, if you take a screenshot​ in emulator, it might exist but not be on screen or the proper size Commented Feb 17, 2018 at 14:25
  • I put the same constraints in code as in storyboard where it works. If I change the color to red, I can see them, perfect. But that is not what I want. Commented Feb 17, 2018 at 17:22

2 Answers 2

2
+100

Problem starts with this var progressBar = ProgressBar(). This invokes the init method and configure() is called. At this point ProgressBar frame width and height is zero.

In your configure() method your are setting line widths.

bottomProgressBar.lineWidth = self.frame.height // 0
topProgressBar.lineWidth = self.frame.height // 0

So lines width are set to zero and thats way it's not being displayed.

To fix this you must update the CAShapeLayer line widths in layoutSubviews() method.

override func layoutSubviews() {
    super.layoutSubviews()

    let path = UIBezierPath()
    path.move(to: CGPoint(x: 0, y: 0))
    path.addLine(to: CGPoint(x: self.frame.width, y: 0))

    bottomProgressBar.lineWidth = self.frame.height // Update lineWidth
    topProgressBar.lineWidth = self.frame.height    // Update lineWidth

    bottomProgressBar.path = path.cgPath
    topProgressBar.path = path.cgPath
}

Why it works for storyboard? Because frame has been set already before configure is called. You can print the frame size in configure and see the difference in both scenarios.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot. It's working now. I will award the bounty when it becomes eligible.
0

It's always a bug in display for custom views, You can add this to viewDidLoad

 self.progressBar.setNeedsDisplay()

Edit:

you can add this line to configure method default background color for the view as the default is transparent

 self.backgroundColor = UIColor.red

I see all colors are white so it's usual not to see anything

8 Comments

Still doesn't work for me, even if I add that line .
what's inside that animate(toValue: 0)
I updated the question with the code. I also try to delete that line inside configure() and still is the same issue. So I am not sure that there is a problem with that method.
it's not a good practice to add animation while you configure the view for display , If you can move that code to viewDidAppear of the mainViewController
Still, same issue. Not sure else can I do.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.