KML KML - 3 months ago 24
iOS Question

CALayer not resizing with Autolayout

I have created a progress bar to be used in a tableView by creating a gradient layer. It works perfectly.

iPhone5:
enter image description here

In order to use the app on multiple devices, I have created the UIView in Storyboard, tagged it and added constraints.
However, when I use the app on an iPhone 6 the CALayer don't resize.

iPhone6:
enter image description here

I find this extremely stupid, but never mind. I have looked around and tried to understand how to solve this for months, but I have come up short. Does ANYONE know how to make CALayers resize with the UIView? Any help would be very much appreciated ! Thank you.

progressBar = cell.contentView.viewWithTag(3) as UIView!
progressBar.layer.cornerRadius = 4
progressBar.layer.masksToBounds = true


// create gradient layer
let gradient : CAGradientLayer = CAGradientLayer()

// create color array
let arrayColors: [AnyObject] = [
UIColor (red: 255/255, green: 138/255, blue: 1/255, alpha: 1).CGColor,
UIColor (red: 110/255, green: 110/255, blue: 118/255, alpha: 1).CGColor]

// set gradient frame bounds to match progressBar bounds
gradient.frame = progressBar.bounds

// set gradient's color array
gradient.colors = arrayColors

//Set progress(progressBar)
var percentageCompleted = 0.6

gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [percentageCompleted, percentageCompleted]
gradient.endPoint = CGPoint(x: 1, y: 0.5)

// replace base layer with gradient layer
progressBar.layer.insertSublayer(gradient, atIndex: 0)

Answer

The default layer of a UIView does resize with its view, but sublayers don't (as you found out). One way to make this work is to create a custom view class, move the code you have in your question to it, and override layoutSublayersOfLayer where you can set the gradient layer to be the same size as the view. Because this code is now in a custom class, I also created a property percentageCompleted (instead of a local variable), and added a willSet clause so the bar's appearance is updated any time you change the percentageCompleted property.

class RDProgressView: UIView {

    private let gradient : CAGradientLayer = CAGradientLayer()

    var percentageCompleted: Double = 0.0 {
        willSet{
            gradient.locations = [newValue, newValue]
        }
    }

    override func awakeFromNib() {
        self.layer.cornerRadius = 4
        self.layer.masksToBounds = true

        // create color array
        let arrayColors: [AnyObject] = [
            UIColor (red: 255/255, green: 138/255, blue: 1/255, alpha: 1).CGColor,
            UIColor (red: 110/255, green: 110/255, blue: 118/255, alpha: 1).CGColor]

        // set gradient's color array
        gradient.colors = arrayColors

        //Set progress(progressBar)
        gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
        gradient.locations = [percentageCompleted, percentageCompleted]
        gradient.endPoint = CGPoint(x: 1, y: 0.5)

        self.layer.insertSublayer(gradient, atIndex: 0)
    }

    override func layoutSublayersOfLayer(layer: CALayer!) {
        super.layoutSublayersOfLayer(layer)
        gradient.frame = self.bounds
    }
}

In IB, you would change the class of your view to RDProgressView (in my example), and in cellForRowAtIndexPath, you would only need to get a reference to the view, and set its percentageCompleted property.

progressBar =  cell.contentView.viewWithTag(3) as RDProgressView! 
progressBar.percentageCompleted = 0.2