2

I'm trying to add gradient to an UILabel in a collectionViewCell. To add gradient I'm using this function:

extension UIView
{
     func gradient(colors: [Any], startPoint: CGPoint, endPoint: CGPoint, opacity: Float, location: [NSNumber]?) {
         let gradientLayer = CAGradientLayer()
         gradientLayer.frame = bounds
         gradientLayer.colors = colors
         gradientLayer.startPoint = startPoint
         gradientLayer.endPoint = endPoint
         gradientLayer.opacity = opacity
         gradientLayer.locations = location
         layer.addSublayer(gradientLayer)
     }
}

and in my collectionViewCell :

@IBOutlet weak var discount: UILabel!

override func awakeFromNib() {
    super.awakeFromNib()
    discount.gradient(colors: [green,blue], startPoint: CGPoint(x: 0.0, y: 0.5), endPoint: CGPoint(x: 1.0, y: 0.5), opacity: 1, location: [0.5,0.5])
}

But it's not showing anything. Adding shadow works fine but adding gradient not showing anything. Why is that happen?

Hos Ap
  • 1,168
  • 2
  • 11
  • 24
  • @meaning-matters I've updated post – Hos Ap Dec 24 '17 at 18:38
  • Why not to subclass UILabel and put that code in the cellForRowAtIndexPath. ? – excitedmicrobe Dec 24 '17 at 18:58
  • The first thing that strikes me is that although you create the gradient layer with a frame as the bounds of the view it will not stay the same. So if/when the view is resized the gradient layer will not be sized accordingly. I can't remember what size the view will start but that could be the issue. – Upholder Of Truth Dec 24 '17 at 19:02
  • @UpholderOfTruth I wanna say yes it's about bounds, but when I use adding shadow (same procedure) it works fine – Hos Ap Dec 24 '17 at 19:31
  • @HosAp reference to subclass UIView and create a gradient view so you don't add multiple gradient layers on top of each other https://stackoverflow.com/questions/24380535/how-to-apply-gradient-to-background-view-of-ios-swift-app/37243106?s=1|57.9597#37243106 – Leo Dabus Dec 24 '17 at 21:53

4 Answers4

4

Update your extension to

extension UIView
{
    func gradient(colors: [CGColor], startPoint: CGPoint, endPoint: CGPoint, opacity: Float, location: [NSNumber]?) {
        let gradientLayer = CAGradientLayer()
        gradientLayer.frame = bounds
        gradientLayer.colors = colors
        gradientLayer.startPoint = startPoint
        gradientLayer.endPoint = endPoint
        gradientLayer.opacity = opacity
        gradientLayer.locations = location
        layer.addSublayer(gradientLayer)
    }
}

Call it from collectionView class as

override func layoutSubviews() {
        discount.gradient(colors: [UIColor.blue.cgColor, UIColor.green.cgColor], startPoint: CGPoint.init(x: 0.5, y: 0.0), endPoint: CGPoint.init(x: 0.5, y: 1.0), opacity: 1.0, location: [0,1])
}

Tested and working.

enter image description here

Md. Ibrahim Hassan
  • 5,359
  • 1
  • 25
  • 45
  • still not working. It's weird adding shadow works but adding gradient not working – Hos Ap Dec 24 '17 at 19:53
  • have you changed the extension function you had used [Any] in colors it should be CGColor – Md. Ibrahim Hassan Dec 24 '17 at 19:58
  • oh, I didn't notice that! I was sending UIColor instead of CGColor. Thanks a lot! – Hos Ap Dec 24 '17 at 20:57
  • The problem with this code is that every time `layoutSubviews()` is called, it will add another gradient sublayer. If you rotate the device, for example, `layoutSubviews()` will be called again for each rotation. That's probably not what you want. Probably the cleanest way to fix that problem is to switch from a UIView extension to a custom subclass of UIView that has a gradientLayer property that gets created at init time, and layoutSubviews simply changes the layer's frame. – Duncan C Dec 24 '17 at 21:18
  • 1
    I completely missed the CGColor issue as I just did it automatically. I agree that this is going to add a new gradient layer each time `layoutSubviews` is called and that is definitely not what you want to be doing. I also agree that subclassing is probably the way to go here which is what we did in v2 (ObjectiveC) and v3 (Swift) of our app. – Upholder Of Truth Dec 24 '17 at 21:40
2

Whenever I add a sublayer to a view I like to make sure the frame is always updated in layoutSubviews:

public override func layoutSubviews() {
    super.layoutSubviews()
    gradientLayer.frame = bounds
}

You'll need to keep a reference to the gradient layer as a property in your view.

Kane Cheshire
  • 1,654
  • 17
  • 20
0

If your goal is making gradient text then you must to the following:

  1. Embed your label in a UIView and connect outlet (say discountContainer)
  2. Add a gradient layer to that view (instead of label).
    discountContainer.gradient(colors: [green,blue], startPoint: CGPoint(x: 0.0, y: 0.5), endPoint: CGPoint(x: 1.0, y: 0.5), opacity: 1, location: [0.5,0.5])
  3. Set the label as the views mask
    discountContainer.mask = discount

Result:

enter image description here


EDITED:

If you need to make gradient only on the background then you should keep the reference to CAGradientLayer.

extension UIView
{
     func gradient(colors: [Any], startPoint: CGPoint, endPoint: CGPoint, opacity: Float, location: [NSNumber]?) -> CAGradientLayer {
         let gradientLayer = CAGradientLayer()
         gradientLayer.frame = bounds
         gradientLayer.colors = colors
         gradientLayer.startPoint = startPoint
         gradientLayer.endPoint = endPoint
         gradientLayer.opacity = opacity
         gradientLayer.locations = location
         layer.addSublayer(gradientLayer)
         return layer
     }
}

and in your collectionViewCell:

@IBOutlet weak var discount: UILabel!
var gradientLayer: CAGradientLayer?

override func awakeFromNib() {
    super.awakeFromNib()
    gradientLayer = discount.gradient(colors: [green,blue], startPoint: CGPoint(x: 0.0, y: 0.5), endPoint: CGPoint(x: 1.0, y: 0.5), opacity: 1, location: [0.5,0.5])
}

public override func layoutSubviews() {
    super.layoutSubviews()
    gradientLayer?.frame = bounds
}
arturdev
  • 10,884
  • 2
  • 39
  • 67
  • I think you have to mix both of my answers :) Embed label in a view, add gradient to that view, and update gradients frame to be equal to that view's bounds in layoutSubviews method – arturdev Dec 24 '17 at 20:17
0

If you have used cocoapods, the Chameleon Framework offers a very easy solution to creating gradient colors. To use it, install cocoapods, then install Chameleon Framework. Put

import ChameleonFramework 

at the top of the UIViewController.

The link for the usage after the installation is below.

https://github.com/ViccAlexander/Chameleon#gradient-colors-1

It looks like it as is easy as the line of code below if you are using Swift.

UIColor(gradientStyle:UIGradientStyle, withFrame:CGRect, andColors:[UIColor])

Just set the background of your UILabel using that line of code above.

Andrew
  • 1