Add a gradient on UIImageView

徘徊边缘 提交于 2020-07-28 13:04:26

问题


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 to photo9 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.
}
}

回答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];



回答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.




回答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



回答4:


extension UIView {

    func addGradient(frame: CGRect) {
        let gradientView = UIView(frame: self.frame)
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = frame
        gradientLayer.colors = [UIColor.clear.cgColor, UIColor.black.cgColor]
        gradientLayer.locations = [0.0, 1.0]
        gradientView.layer.insertSublayer(gradientLayer, at: 0)
        addSubview(gradientView)
    }
}

How to use

let headerView = UIImageView(frame: viewFrame)

// Setup frame
let gradientViewFrame = yourGradientFrame;
headerView.addGradient(frame: gradientViewFrame)



回答5:


These solutions didn't work properly for me. This answer was inspired from this. I have IBOutlet and it's like,

@IBOutlet weak var imageView: UIImageView!

Then I added this piece of code.

let gradient = CAGradientLayer()
gradient.frame = imageView.bounds
gradient.contents = imageView.image?.cgImage
gradient.colors = [UIColor.green.cgColor, UIColor.blue.cgColor]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
imageView.layer.addSublayer(gradient)

It works as I wanted. The most important point is adding contents to gradient layer.



来源:https://stackoverflow.com/questions/40405798/add-a-gradient-on-uiimageview

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!