skip to Main Content

I have added two views inside a stack view. I am having that stack view inside my tableViewCell. Now my stackView is placed in the centre of the content view with width and height. I want to apply gradient color inside my view which is inside my stackView .

Just look at the view below In this case I am giving view a background colour of red and green which I don’t want. I want to apply gradient of four colours to this views.
enter image description here

2

Answers


  1. Chosen as BEST ANSWER

    Create GradientLayer on your tableViewCell(collectionViewCell)

    1. Create CAGradientLayer Instance on your tableViewCell

    class MyCell: UITableViewCell {
        let gradientLayer = CAGradientLayer()
    }
    

    2. Set the layer frame to view's bounds from layoutSublayers(of:)

    If you set your layer's frame some other place, frame becomes weird.

    class MyCell: UITableViewCell {
        let gradientLayer = CAGradientLayer()
        
        // Setting it from layoutSubViews also fine.
        override func layoutSublayers(of layer: CALayer) {
            super.layoutSublayers(of: self.layer)
            gradientLayer.frame = yourView.bounds
        }
    }
    

    3. Add GradientLayer on the View.

    Here is my UIView extension for adding gradient layer.

    extension UIView {
        func addGradient(with layer: CAGradientLayer, gradientFrame: CGRect? = nil, colorSet: [UIColor],
                         locations: [Double], startEndPoints: (CGPoint, CGPoint)? = nil) {
            layer.frame = gradientFrame ?? self.bounds
            layer.frame.origin = .zero
    
            let layerColorSet = colorSet.map { $0.cgColor }
            let layerLocations = locations.map { $0 as NSNumber }
    
            layer.colors = layerColorSet
            layer.locations = layerLocations
    
            if let startEndPoints = startEndPoints {
                layer.startPoint = startEndPoints.0
                layer.endPoint = startEndPoints.1
            }
    
            self.layer.insertSublayer(layer, above: self.layer)
        }
    }
    
    * Option 1 - Add it from layoutSublayers(of:)
    class MyCell: UITableViewCell {
        let gradientLayer = CAGradientLayer()
        
        override func layoutSublayers(of layer: CALayer) {
            super.layoutSublayers(of: self.layer)
            gradientLayer.frame = yourView.bounds
            
            let colorSet = [UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1.0),
                            UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0)]
            let location = [0.2, 1.0]
            
            yourView.addGradient(with: gradientLayer, colorSet: colorSet, locations: location)
        }
    }
    
    * Option 2 - Add it from cellForRowAt

    For example, if we have cellForRowAt and like this,

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let cell = tableView.dequeueReusableCell(withIdentifier: "yourCellId", for: indexPath) as? MyCell {
          cell.configureCell(content: someItems[indexPath.row])
      }
      return UITableViewCell()
    }
    

    Now, we can handle gradientLayer from configureCell(content:).

    class MyCell: UITableViewCell {
        let gradientLayer = CAGradientLayer()
        
        func configureCell(content: YourType) {
            let colorSet = [UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1.0),
                            UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0)]
            let location = [0.2, 1.0]
            yourView.addGradient(with: gradientLayer, colorSet: colorSet, locations: location)
        }
        
        override func layoutSublayers(of layer: CALayer) {
            super.layoutSublayers(of: self.layer)
            gradientLayer.frame = yourView.bounds
        }
    }
    

  2. Here is the code of a simple progress bar that I have written in Swift:

    import Foundation
    import Cocoa
    
    public class GradientProgressView : NSView
    {
        // MARK: - Fields
        private let bgLayer = CAShapeLayer()
        private var progressLayer = CAShapeLayer()
        private var gradientLayer = CAGradientLayer()
        private var blockLayer = CAShapeLayer()
        private var mValue: CGFloat = 0.0
        
        // MARK: - Properties
        public var colors: [NSColor] = [NSColor.systemGreen, NSColor.systemYellow, NSColor.sytemRed]
        
        public var value: CGFloat
        {
            set
            {
                self.mValue = newValue
                
                if self.mValue > 1.0
                {
                    self.mValue = 1.0
                }
                if self.mValue < 0.0 || self.mValue.isNaN || self.mValue.isInfinite
                {
                    self.mValue = 0.0
                }
                
                self.progressLayer.strokeEnd = self.mValue
            }
            get
            {
                return self.mValue
            }
        }
        
        // MARK: - Overrides
        public override func layout()
        {
            super.layout()
            
            self.initialize()
        }
        
        // MARK: - Helper
        fileprivate func initialize()
        {
            self.wantsLayer = true
            
            self.layer?.masksToBounds = true // Optional
            self.layer?.cornerRadius = self.bounds.height / 2.0 // Optional
            
            self.bgLayer.removeFromSuperlayer()
            self.bgLayer.contentsScale = NSScreen.scale
            self.bgLayer.fillColor = NSColor.separatorColor.cgColor
            self.bgLayer.path = NSBezierPath(rect: self.bounds).cgPath
            self.layer?.addSublayer(self.bgLayer)
            
            self.blockLayer.removeAllAnimations()
            self.blockLayer.removeFromSuperlayer()
            
            let path = NSBezierPath()
            path.move(to: CGPoint(x: self.bounds.height / 2.0, y: self.bounds.height / 2.0))
            path.line(to: CGPoint(x: self.bounds.width - self.bounds.height / 2.0, y: self.bounds.height / 2.0))
            
            self.progressLayer.path = path.cgPath
            self.progressLayer.strokeColor = NSColor.black.cgColor
            self.progressLayer.fillColor = nil
            self.progressLayer.lineWidth = self.bounds.height
            self.progressLayer.lineCap = .round
            self.progressLayer.strokeStart = 0.0
            self.progressLayer.strokeEnd = 0.0
            self.progressLayer.contentsScale = NSScreen.scale
            
            self.gradientLayer.removeAllAnimations()
            self.gradientLayer.removeFromSuperlayer()
            self.gradientLayer.contentsScale = NSScreen.scale
            self.gradientLayer.colors = self.colors.map({ $0.cgColor })
            self.gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
            self.gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
            self.gradientLayer.mask = self.progressLayer
            self.gradientLayer.frame = self.bounds
            self.gradientLayer.contentsScale = NSScreen.scale
            
            self.layer?.addSublayer(self.gradientLayer)
        }
    }
    

    This progress bar supports multiple colors.
    Simply replace your progress bar logic with the one I posted above (or add the logic for the triangle that is shown below the progress bar).

    Preview:

    Preview

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search