Add a gradient on UIImageView
Solution 1
I would suggest putting a UIView
with the gradient on top of the UIImageView
:
@IBOutlet weak var shanghaiImage: UIImageView!
let view = UIView(frame: profileImageView.frame)
let gradient = CAGradientLayer()
gradient.frame = view.frame
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
gradient.locations = [0.0, 1.0]
view.layer.insertSublayer(gradient, at: 0)
shanghaiImage.addSubview(view)
shanghaiImage.bringSubview(toFront: view)
Objective-C:
UIView *view = [[UIView alloc] initWithFrame: profileImageView.frame];
CAGradientLayer *gradient = [[CAGradientLayer alloc] init];
gradient.frame = view.frame;
gradient.colors = @[ (id)[[UIColor clearColor] CGColor], (id)[[UIColor blackColor] CGColor] ];
gradient.locations = @[@0.0, @1.0];
[view.layer insertSublayer: gradient atIndex: 0];
[shanghaiImage addSubview: view];
[shanghaiImage bringSubviewToFront: view];
Solution 2
You can use extension for swift 3, swift 4 and swift 5
Create a new file for your extension of UIImageView like UIImageView_extension.swift and set into this code:
UIImageView is extends UIView so if you change UIImageView to UIView then it become more dynamic i.e can used by all components who extents UIView. So I used UIView instead of UIImageView.
import UIKit
extension UIView{
// For insert layer in Foreground
func addBlackGradientLayerInForeground(frame: CGRect, colors:[UIColor]){
let gradient = CAGradientLayer()
gradient.frame = frame
gradient.colors = colors.map{$0.cgColor}
self.layer.addSublayer(gradient)
}
// For insert layer in background
func addBlackGradientLayerInBackground(frame: CGRect, colors:[UIColor]){
let gradient = CAGradientLayer()
gradient.frame = frame
gradient.colors = colors.map{$0.cgColor}
self.layer.insertSublayer(gradient, at: 0)
}
}
and in your ViewController.swift you can use it:
class myViewController: UIViewController{
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
imageView.addBlackGradientLayerInBackground(frame: view.bounds, colors:[.clear, .black])
//Alternative
//imageView.addBlackGradientLayerInBackground(frame: imageView.frame, colors: [.clear, .black])
}
}
This function needs a frame, so you only need a frame from view or yourself imageView. Always think like generic function, then you can change the gradient colors without troubles in the future in other views.
Solution 3
In Swift 5.1 I have create a custom class that inherits from UIImageView and supports multiple gradient configurations.
import UIKit
import SnapKit
class GradientImageView: UIImageView {
//MARK: - View model
enum GradientDirection {
case upDown
case downUp
case leftRight
case rightLeft
case topLeftBottomRight
case topRightBottomLeft
case bottomLeftTopRight
case bottomRightTopLeft
}
//MARK: - Properties
var colors: [UIColor] = [] {
didSet {
updateGradient()
}
}
private var cgColors: [CGColor] {
return colors.map({ $0.cgColor })
}
var gradientDirection: GradientDirection = .downUp {
didSet {
updateGradient()
}
}
private lazy var gradientLayer: CAGradientLayer = {
let layer = CAGradientLayer()
layer.shouldRasterize = true
return layer
}()
//MARK: UI
private lazy var overlayView: UIView = { return UIView() }()
//MARK: - Constructor
init(colors: [UIColor], gradientDirection: GradientDirection) {
super.init(frame: .zero)
self.colors = colors
self.gradientDirection = gradientDirection
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
//MARK: - Lifecycle methods methods
extension GradientImageView {
override func didMoveToSuperview() {
super.didMoveToSuperview()
if superview != nil {
setupUI()
updateGradient()
}
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = overlayView.frame
}
}
//MARK: - Private methods
private extension GradientImageView {
func setupUI() {
addSubview(overlayView)
//With Snapkit
overlayView.snp.makeConstraints { (maker) in
maker.edges.equalToSuperview()
}
//Without Snapkit
//overlayView.translatesAutoresizingMaskIntoConstraints = false
//overlayView.topAnchor.constraint(equalTo: overlayView.superview!.topAnchor).isActive = true
//overlayView.leftAnchor.constraint(equalTo: overlayView.superview!.leftAnchor).isActive = true
//overlayView.bottomAnchor.constraint(equalTo: overlayView.superview!.bottomAnchor).isActive = true
//overlayView.rightAnchor.constraint(equalTo: overlayView.superview!.rightAnchor).isActive = true
overlayView.layer.addSublayer(gradientLayer)
}
func updateGradient() {
gradientLayer.colors = cgColors
switch gradientDirection {
case .upDown:
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
case .downUp:
gradientLayer.startPoint = CGPoint(x: 0.5, y: 1)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 0)
case .leftRight:
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
case .rightLeft:
gradientLayer.startPoint = CGPoint(x: 1, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0, y: 0.5)
case .topLeftBottomRight:
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1)
case .topRightBottomLeft:
gradientLayer.startPoint = CGPoint(x: 1, y: 0)
gradientLayer.endPoint = CGPoint(x: 0, y: 1)
case .bottomLeftTopRight:
gradientLayer.startPoint = CGPoint(x: 0, y: 1)
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
case .bottomRightTopLeft:
gradientLayer.startPoint = CGPoint(x: 1, y: 1)
gradientLayer.endPoint = CGPoint(x: 0, y: 0)
}
}
}
USAGE
let gradientImageView = GradientImageView(colors: [YOUR COLORS], gradientDirection: .upDown)
gradientImageView.image = //YOUR Image
Solution 4
That should be the correct answer
extension UIImageView {
func makeGradient() {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.contents = self.image?.cgImage
gradient.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
gradient.startPoint = CGPoint(x: 0.5, y: 0.5)
gradient.endPoint = CGPoint(x: 0.5, y: 1)
self.layer.addSublayer(gradient)
}
}
Alexandre
Updated on July 09, 2022Comments
-
Alexandre almost 2 years
I am trying to add a sublayer on my
UIImageView
but it doesn't work.- I have a set of 10 images named from
photo0
tophoto9
and I display it with a timer of 5s. - The outlet
shanghaiImage
is my background
I would like to add a gradient on top of this marty like: transparent (top) to black (bottom).
Thanks for the help :)
Here is my code in Swift 3.
This part is fine :
import UIKit class ViewController: UIViewController { @IBOutlet weak var shanghaiImage: UIImageView! // beginning index var _curentImageIndex:Int = 0 // number of images let NUMBER_OF_IMAGES:Int = 10 // creation of the Timer var _uiTimer:Timer? override func viewDidLoad() { super.viewDidLoad() showPhoto(atIndex: _curentImageIndex) } // MARK TIMER --------- func selectNewTimer(){ if let existingTimer:Timer = _uiTimer{ existingTimer.invalidate() } _uiTimer = Timer.scheduledTimer(timeInterval: 5, target: self, selector: #selector(ViewController.showNextImage), userInfo: nil, repeats: true) }
It's here where there is a problem. I don't know why it's not working.
// MARK PHOTO --------- func showPhoto(atIndex index:Int){ let photoName:String = "photo\(index)" shanghaiImage.image = UIImage(named: photoName) let gradient = CAGradientLayer() gradient.frame = shanghaiImage.bounds let startColor = UIColor(colorLiteralRed: 0, green: 0, blue: 0, alpha: 1) let endColor = UIColor.black gradient.colors = [startColor, endColor] shanghaiImage.layer.insertSublayer(gradient, at: 0) _curentImageIndex = index selectNewTimer() } func showNextImage() { var nextPhotoIndex:Int = _curentImageIndex + 1 if nextPhotoIndex >= NUMBER_OF_IMAGES { nextPhotoIndex = 0 } showPhoto(atIndex: nextPhotoIndex) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
- I have a set of 10 images named from
-
Caleb Kleveter over 7 years@DeepakKumar I updated my answer to have objective-c in it. My Obj-C is a bit rusty and Xcode is out of commission right now, so it might have some bugs.
-
Bogdan over 6 yearsSince this is an extension for
UIImageView
instead of adding the parameter frame, you can useself.frame
property. The code should be something like:gradient.frame = self.frame
. Anyway, thanks for the solution, it really works! -
Khushbu Desai over 6 years@CalebKleveter I have used this code but now I want to save this Image in my Gallery , How do I achieve this ?
-
Caleb Kleveter over 6 years@KhushbuDesai If you want to save an image that you added a gradient to, it will be much more involved because you will have to edit the actual image itself. I would suggest Googling around to try to find something.
-
Khushbu Desai over 6 years@CalebKleveter I had already google it but that's only solution I found is ScreenShot of that image and its not worth it . that's why i am asking you here.
-
Caleb Kleveter over 6 years@KhushbuDesai Look at this: hackingwithswift.com/example-code/media/…
-
Aakash Dave over 6 yearsHey, I tried this, works well. The only things I am facing problem with this is that the gradient doesnt fully cover my image, It leaves a bit of image from the right side
-
Iris over 6 yearsI agree with @eraser2021999, the line defining the frame could be gradient.frame = self.frame instead, and the method should receive no parameter.
-
gandhi Mena over 6 yearsHi, i fixed the the problem for fully cover the image. Thanks for your comments. @eraser2021999, Aakash, Iris.
-
ios_dev about 5 years@gandhiMena how did you solve that not fully cover image stuff?
-
Howard Shere about 5 yearsDon't use self.frame, use CGRect(origin: .zero, size: self.frame.size)
-
brokenrhino over 4 yearsIn Swift 4.2 - 'bringSubview(toFront:)' has been renamed to 'bringSubviewToFront(_:)'
-
Jignesh Kanani almost 4 yearsHow can i add snapit?
-
Reimond Hill almost 4 yearsgithub.com/SnapKit/SnapKit But I have added without
-
Mert Köksal over 3 yearsI have tried this. It adds a gradient but the image is blank.
-
ChuckZHB over 2 yearsI think this line shoud be
let view = UIView(frame: profileImageView.bounds)
. UseprofileImageView.bounds
instead ofprofileImageView.frame
, otherwise this gradient view will shift off its parentViewshanghaiImage
. -
ChuckZHB over 2 yearsI found put the call in
viewDidLoad
will not take effect, instead I put it underviewDidLayoutSubview
do the things.