1

I want to apply color gradient(from top to bottom) to an image named 'newspaperImage'. This is my code using swift language.

@IBOutlet weak var newspaperImage: UIImageView!

func imageGradient(){
    let newspaperview = UIView(frame: newspaperImage.frame)
    let gradient = CAGradientLayer()
    gradient.frame = newspaperview.bounds
    let startColor = UIColor(red: 30, green: 113, blue: 79, alpha: 0).cgColor
    let endColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
    gradient.colors = [startColor, endColor]
    newspaperview.layer.insertSublayer(gradient, at: 0)
    newspaperImage.addSubview(newspaperview)
    newspaperImage.bringSubviewToFront(newspaperview)
}

override func viewDidLoad() {
    super.viewDidLoad()

    imageGradient()
}

After running, I find it doesn't work at all. Where's the problem?

Mojie11
  • 85
  • 1
  • 3
  • 11

1 Answers1

3

First lets start with UIColor(red:green:blue:alpha) expects the parameters to be normalised values of 0-1.

So...

let startColor = UIColor(red: 30, green: 113, blue: 79, alpha: 0).cgColor

should be

let startColor = UIColor(red: 30/255, green: 113/255, blue: 79/255, alpha: 0).cgColor

Next, I took your basic code and dumped into Playground

let image = UIImage(named: "Miho_Small.png")

let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
imageView.contentMode = .scaleAspectFit
imageView.image = image

let gradient = CAGradientLayer()
gradient.frame = imageView.bounds
let startColor = UIColor(red: 30/255, green: 113/255, blue: 79/255, alpha: 0).cgColor
let endColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
gradient.colors = [startColor, endColor]
imageView.layer.insertSublayer(gradient, at: 0)

imageView

And it generated...

Surprise

Okay, that's not what I was expecting, but it's not entirely unsurprising. The CALayer is been painted over the top of the image, because the image is been painted by the UIView's draw function.

Okay, so how can we fix it? With out going to the extend of making a new images and painting the gradient and image into it, you could make use of a "background" UIView, onto which the layer and UIImageView are applied...

let image = UIImage(named: "Miho_Small.png")

let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
imageView.contentMode = .scaleAspectFit
imageView.image = image
imageView.backgroundColor = nil

let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))

let gradient = CAGradientLayer()
gradient.frame = backgroundView.bounds
let startColor = UIColor(red: 30/255, green: 113/255, blue: 79/255, alpha: 0).cgColor
let endColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
gradient.colors = [startColor, endColor]
backgroundView.layer.insertSublayer(gradient, at: 0)

backgroundView.addSubview(imageView)

backgroundView

which generates...

Better

And, that's probably more along the lines of what you're trying to achieve...at a guess

:(, the two views can not be overlapped

I don't "overlap", so to speak, I add the UIImageView onto the backgroundView, which contains the CALayer. Remember, the view's frame is relative to it's parent's coordinate space.

In almost all cases, I prefer to use auto layout, and I prefer to use story boards, but those are difficult to make int an answer, so I've done it by hand for this example...

Example

class ViewController: UIViewController {

  override func viewDidLoad() {
    super.viewDidLoad()
    let image = UIImage(named: "Miho_Small.png")

    let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 250, height: 250))
    imageView.contentMode = .scaleAspectFit
    imageView.image = image
    imageView.backgroundColor = nil

    let backgroundView = UIView(frame: CGRect(x: 0, y: 0, width: 250, height: 250))
    backgroundView.translatesAutoresizingMaskIntoConstraints = false

    let gradient = CAGradientLayer()
    gradient.frame = backgroundView.bounds
    let startColor = UIColor(red: 30/255, green: 113/255, blue: 79/255, alpha: 0).cgColor
    let endColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor
    gradient.colors = [startColor, endColor]
    backgroundView.layer.insertSublayer(gradient, at: 0)

    backgroundView.addSubview(imageView)

    view.addSubview(backgroundView)

    imageView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true
    imageView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true
    imageView.topAnchor.constraint(equalTo: backgroundView.topAnchor).isActive = true
    imageView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor).isActive = true

    backgroundView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    backgroundView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

    backgroundView.addConstraint(NSLayoutConstraint(item: backgroundView,
                                                    attribute: .width,
                                                    relatedBy: .equal,
                                                    toItem: nil,
                                                    attribute: .notAnAttribute,
                                                    multiplier: 1.0,
                                                    constant: 250))
    backgroundView.addConstraint(NSLayoutConstraint(item: backgroundView,
                                                    attribute: .width,
                                                    relatedBy: .equal,
                                                    toItem: backgroundView,
                                                    attribute: .height,
                                                    multiplier: 1.0,
                                                    constant: 0))


  }


}

For what it's worth, you can checkout the project I used to test the code from GitHub - TestCALayerGradientAndImageOverlay

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Thank you very much! Your answer solved my problem :) – Mojie11 Dec 15 '18 at 08:27
  • Sorry to bother you but I have another problem :(, the two views can not be overlapped. You can see my screen shot image in the link below. [link](https://i.stack.imgur.com/jkKaE.png) – Mojie11 Dec 15 '18 at 09:03
  • I didn’t overlay them, I added both to a base view - personally, I’d use autolayout to pin them to the edges. You need to make sure the frames of the child views match the bounds of the parent view – MadProgrammer Dec 15 '18 at 09:46
  • Hey! Is there a way to use gradient to change only the alpha value and not add any color to the gradient? Like the image should fade out from bottom to top? – Saad Tahir Sep 29 '20 at 15:55