Mojie11
Mojie11

Reputation: 105

Applying gradient to UIImage

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?

Upvotes: 1

Views: 7853

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347334

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

Upvotes: 4

Related Questions