Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why gradient doesn't cover the whole width of the view

Tags:

ios

swift

I'm trying to apply a gradient to a view which is constraint to the top, left and right of the main screen but for some reason the gradient doesn't cover the whole width of the view that is applied to (see the yellow in the picture).

class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let gradient = CAGradientLayer()
        gradient.colors = [UIColor.blue.cgColor, UIColor.white.cgColor]
        gradient.startPoint = CGPoint(x:00, y:00)
        gradient.endPoint = CGPoint(x:0, y:0.6)
        gradient.frame = myView.bounds
        myView.clipsToBounds = true
        myView.layer.insertSublayer(gradient, at: 0)
    }
}

What am I doing wrong?

enter image description here

like image 933
fs_tigre Avatar asked Jun 24 '17 14:06

fs_tigre


People also ask

Why gradient doesn't cover the whole width of the view?

The problem is likely that you are adding the gradient layer in viewDidLoad() . A view doesn't get set to it's final size until after viewDidLoad() . Your view controller may be set up in your XIB/Storyboard for a different screen-size than you're running it on.


Video Answer


3 Answers

The problem is likely that you are adding the gradient layer in viewDidLoad(). A view doesn't get set to it's final size until after viewDidLoad().

Your view controller may be set up in your XIB/Storyboard for a different screen-size than you're running it on. (Say you have it set to iPhone SE size, but you're running it on a 6. The 6's screen is a little wider, so the layer will get set to the width of the iPhone SE, when the view is first loaded. Then the view will be resized, but the layer's size will never be adjusted.)

You should implement the UIViewController method viewDidLayoutSubviews(), and in that method, adjust the layer's frame:

override func viewDidLayoutSubviews() {
  gradientLayer.frame = self.view.bounds
}

That way if the view gets resized (due to auto-rotation, for example) the gradient layer will be automatically adjusted accordingly.

EDIT:

As pointed out by Sulthan, changes to a layer's frame are animated by default. You should probably wrap the frame change in a CATransaction.begin/CATransaction.end and disable actions, like below:

override func viewDidLayoutSubviews() {
  CATransaction.begin()
  CATransaction.setDisableActions(true)
  gradientLayer.frame = self.view.bounds
  CATransaction.commit()
}
like image 124
Duncan C Avatar answered Oct 19 '22 23:10

Duncan C


You needn't set the start and end point, given your goal is to have the gradient span the entire view. You're already setting that with `

gradientLayer.frame = self.view.bounds

`

import UIKit
class ViewController: UIViewController {

    @IBOutlet weak var myView: UIView!
    var gradientLayer: CAGradientLayer!
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
        createGradientLayer()
    }

    func createGradientLayer() {
        gradientLayer = CAGradientLayer()

        gradientLayer.frame = self.view.bounds

        gradientLayer.colors = [UIColor.blueColor().CGColor, UIColor.whiteColor().CGColor]

        self.view.layer.addSublayer(gradientLayer)
    }
}
like image 35
Martin Muldoon Avatar answered Oct 19 '22 23:10

Martin Muldoon


You can turn it to a UIView. So it will resize automatically and can be seen directly in the Storyboard:

@IBDesignable
final class GradientView: UIView {

    @IBInspectable var firstColor: UIColor = .clear { didSet { updateView() } }
    @IBInspectable var secondColor: UIColor = .clear { didSet { updateView() } }

    @IBInspectable var startPoint: CGPoint = CGPoint(x: 0, y: 0) { didSet { updateView() } }
    @IBInspectable var endPoint: CGPoint = CGPoint(x: 1, y: 1) { didSet { updateView() } }

    override class var layerClass: AnyClass { get { CAGradientLayer.self } }

    override func layoutSubviews() {
        super.layoutSubviews()
        updateView()
        layer.frame = bounds
    }

    private func updateView() {
        let layer = self.layer as! CAGradientLayer
        layer.colors = [firstColor, secondColor].map {$0.cgColor}
        layer.startPoint = startPoint
        layer.endPoint = endPoint
    }
}

Preview

like image 23
Mojtaba Hosseini Avatar answered Oct 19 '22 22:10

Mojtaba Hosseini