ChatGPT解决这个技术问题 Extra ChatGPT

Programmatically create a UIView with color gradient

I'm trying to generate a view with a gradient color background (A solid color to transparent) at runtime. Is there a way of doing that?

This is a helpful little tool that creates gradient code for you itunes.apple.com/gb/app/gradient-creator/id1031070259?mt=12
Creating Gradient Colors Using CAGradientLayer appcoda.com/cagradientlayer

C
Community

Objective-C:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
CAGradientLayer *gradient = [CAGradientLayer layer];

gradient.frame = view.bounds;
gradient.colors = @[(id)[UIColor whiteColor].CGColor, (id)[UIColor blackColor].CGColor];

[view.layer insertSublayer:gradient atIndex:0];

Swift:

let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 50))
let gradient = CAGradientLayer()

gradient.frame = view.bounds
gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor]

view.layer.insertSublayer(gradient, at: 0)

Info: use startPoint and endPoint to change direction of gradient.

If there are any other views added onto this UIView (such as a UILabel), you may want to consider setting the background color of those UIView’s to [UIColor clearColor] so the gradient view is presented instead of the background color for sub views. Using clearColor has a slight performance hit.


+1 for shortest amount of code out of all solutions. I just added to my existing view controller, changed the 2 refs to view to self.view and it worked like a charm :)
Also relevant is where to add this code. I needed the following post to get this to work: stackoverflow.com/a/20059268/1480518
This helped me a lot! Missed the CGColor part everywhere else! Thanks
Don't forget to use 'startPoint' and 'endPoint' to change gradient direction. Default values are (0.5, 0) and (0.5,1) - from top to bottom direction.
When my UIView is using AutoLayout, I also had to call gradient.frame = view.bounds in viewDidAppear() and in didRotateFromInterfaceOrientation() or else the gradient wouldn't be sized properly.
Y
Yuchen

You can create a custom class GradientView:

Swift 5

class GradientView: UIView {
    override open class var layerClass: AnyClass {
       return CAGradientLayer.classForCoder()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        let gradientLayer = layer as! CAGradientLayer
        gradientLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
    }
}

In the storyboard, set the class type to any view that you want to have gradient background:

https://i.stack.imgur.com/mAgG0.png

This is better in the following ways:

No need to set frame of CLayer

Use NSConstraint as usual on the UIView

Don't need to create sublayers (less memory use)


On Swift 3 the layerClass is no longer a function, it's a property so you have to override it like a property: stackoverflow.com/questions/28786597/…. Also you have to implement override init(frame: CGRect), check this answer: stackoverflow.com/questions/27374330/…. Thanks!
Hello @LaloLoop, thanks for the comment! I've updated the code. I don't think I have to override init(frame:CGRect) though. I tested the updated code and it works fine.
@Yuchen Zhong It crashing in IB, but works in the app. Any reasons why it is not rendered in IB if @IBDesignable is used?
@AlexanderVolkov Probably some other issue? We've been using it like so in the storyboard and don't seem to have any problem though. You can debug your storyboard and see which line is crashing it.
Best solution !
J
Jaywant Khedkar

Try This it worked like a charm for me,

Objective C

I have set RGB gradient background Color to UIview

   UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,35)];
   CAGradientLayer *gradient = [CAGradientLayer layer];
   gradient.frame = view.bounds;
   gradient.startPoint = CGPointZero;
   gradient.endPoint = CGPointMake(1, 1);
   gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:34.0/255.0 green:211/255.0 blue:198/255.0 alpha:1.0] CGColor],(id)[[UIColor colorWithRed:145/255.0 green:72.0/255.0 blue:203/255.0 alpha:1.0] CGColor], nil];
   [view.layer addSublayer:gradient];

https://i.stack.imgur.com/dmWrH.png

UPDATED :- Swift3 +

Code :-

 var gradientView = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 35))
 let gradientLayer:CAGradientLayer = CAGradientLayer()
 gradientLayer.frame.size = self.gradientView.frame.size
 gradientLayer.colors = 
 [UIColor.white.cgColor,UIColor.red.withAlphaComponent(1).cgColor] 
//Use diffrent colors
 gradientView.layer.addSublayer(gradientLayer)

https://i.stack.imgur.com/g33TQ.png

You can add starting and end point of gradient color.

  gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0)
  gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)

https://i.stack.imgur.com/2diim.png

For more details description refer CAGradientLayer Doc

Hope this is help for some one .


S
Sam Fischer

This is my recommended approach.

To promote reusability, I'd say create a category of CAGradientLayer and add your desired gradients as class methods. Specify them in the header file like this :

#import <QuartzCore/QuartzCore.h>

@interface CAGradientLayer (SJSGradients)

+ (CAGradientLayer *)redGradientLayer;
+ (CAGradientLayer *)blueGradientLayer;
+ (CAGradientLayer *)turquoiseGradientLayer;
+ (CAGradientLayer *)flavescentGradientLayer;
+ (CAGradientLayer *)whiteGradientLayer;
+ (CAGradientLayer *)chocolateGradientLayer;
+ (CAGradientLayer *)tangerineGradientLayer;
+ (CAGradientLayer *)pastelBlueGradientLayer;
+ (CAGradientLayer *)yellowGradientLayer;
+ (CAGradientLayer *)purpleGradientLayer;
+ (CAGradientLayer *)greenGradientLayer;

@end

Then in your implementation file, specify each gradient with this syntax :

+ (CAGradientLayer *)flavescentGradientLayer
{
    UIColor *topColor = [UIColor colorWithRed:1 green:0.92 blue:0.56 alpha:1];
    UIColor *bottomColor = [UIColor colorWithRed:0.18 green:0.18 blue:0.18 alpha:1];

    NSArray *gradientColors = [NSArray arrayWithObjects:(id)topColor.CGColor, (id)bottomColor.CGColor, nil];
    NSArray *gradientLocations = [NSArray arrayWithObjects:[NSNumber numberWithInt:0.0],[NSNumber numberWithInt:1.0], nil];

    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.colors = gradientColors;
    gradientLayer.locations = gradientLocations;

    return gradientLayer;
}

Then simply import this category in your ViewController or any other required subclass, and use it like this :

CAGradientLayer *backgroundLayer = [CAGradientLayer purpleGradientLayer];
backgroundLayer.frame = self.view.frame;
[self.view.layer insertSublayer:backgroundLayer atIndex:0];

where should you put the code to use it? what lifecycle method? viewdidappear makes it appear a second too late. viewdidload and viewdidappear cause it to not work while rotating. viewdidlayoutsubviews works better for rotation but still ends up looking a little choppy.
I personally put it in viewDidLoad
When i put it in viewDidLoad, it didn't work if the app was launched in landscape orientation. My solution was to make the background layer frame wider than the view.frame to compensate for other orientations.
viewDidLoad works, you just have to also set the AutoResizingMask using [backgroundLayer setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];
E
Elijah
extension UIView {

    func applyGradient(isVertical: Bool, colorArray: [UIColor]) { 
        layer.sublayers?.filter({ $0 is CAGradientLayer }).forEach({ $0.removeFromSuperlayer() })
         
        let gradientLayer = CAGradientLayer()
        gradientLayer.colors = colorArray.map({ $0.cgColor })
        if isVertical {
            //top to bottom
            gradientLayer.locations = [0.0, 1.0]
        } else {
            //left to right
            gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
            gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
        }
        
        backgroundColor = .clear
        gradientLayer.frame = bounds
        layer.insertSublayer(gradientLayer, at: 0)
    }

}

USAGE

someView.applyGradient(isVertical: true, colorArray: [.green, .blue])

b
blauzahn

Since I only needed one type of gradient throughout my app I created a subclass of UIView and preconfigured the gradient layer on initialization with fixed colors. The initializers of UIView call the configureGradientLayer-method, which configures the CAGradientLayer:

DDGradientView.h:

#import <UIKit/UIKit.h>

@interface DDGradientView : UIView {

}
@end

DDGradientView.m:

#import "DDGradientView.h"

@implementation DDGradientView

// Change the views layer class to CAGradientLayer class
+ (Class)layerClass
{
    return [CAGradientLayer class];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if(self) {
        [self configureGradientLayer];
    }
    return self;
}

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if(self) {
        [self configureGradientLayer];
    }
    return self;
}

// Make custom configuration of your gradient here   
- (void)configureGradientLayer {
    CAGradientLayer *gLayer = (CAGradientLayer *)self.layer;
    gLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor lightGrayColor] CGColor], nil];
}
@end

The method nameinitGradientLayer is similar to an initializer. I don't think it's very suitable.
@DawnSong Good point, I changed it to 'configureGradientLayer'.
f
flocbit

I've extended the accepted answer a little using Swift's extension functionality as well as an enum.

Oh and if you are using Storyboard like I do, make sure to call gradientBackground(from:to:direction:) in viewDidLayoutSubviews() or later.

Swift 3

enum GradientDirection {
    case leftToRight
    case rightToLeft
    case topToBottom
    case bottomToTop
}

extension UIView {
    func gradientBackground(from color1: UIColor, to color2: UIColor, direction: GradientDirection) {
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = [color1.cgColor, color2.cgColor]

        switch direction {
        case .leftToRight:
            gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
            gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
        case .rightToLeft:
            gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
            gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
        case .bottomToTop:
            gradient.startPoint = CGPoint(x: 0.5, y: 1.0)
            gradient.endPoint = CGPoint(x: 0.5, y: 0.0)
        default:
            break
        }

        self.layer.insertSublayer(gradient, at: 0)
    }
}

This applies to every solution but just a reminder that self.clipsToBounds needs to be true or gradient.cornerRadius needs to be set to self.layer.cornerRadius if self has rounded corners.
D
Dustin Williams

Swift Implementation:

var gradientLayerView: UIView = UIView(frame: CGRectMake(0, 0, view.bounds.width, 50))
var gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = gradientLayerView.bounds
gradient.colors = [UIColor.grayColor().CGColor, UIColor.clearColor().CGColor]
gradientLayerView.layer.insertSublayer(gradient, atIndex: 0)
self.view.layer.insertSublayer(gradientLayerView.layer, atIndex: 0)

G
Guillaume Legrain

I have implemented this in swift with an extension:

Swift 3

extension UIView {
    func addGradientWithColor(color: UIColor) {
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = [UIColor.clear.cgColor, color.cgColor]

        self.layer.insertSublayer(gradient, at: 0)
    }
}

Swift 2.2

extension UIView {
    func addGradientWithColor(color: UIColor) {
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = [UIColor.clearColor().CGColor, color.CGColor]

        self.layer.insertSublayer(gradient, atIndex: 0)
    }
}

No I can set a gradient on every view like this:

myImageView.addGradientWithColor(UIColor.blue)

Swift3 call must be like : myImageView.addGradientWithColor(color: UIColor.blue)
E
Etienne Noël

A Swift Approach

This answer builds on the answers above and provides implementation for dealing with the problem of the gradient not being properly applied during rotation. It satisfies this problem by changing the gradient layer to a square so that rotation in all directions results in a correct gradient. The function signature includes a Swift variadic argument that allows one to pass in as many CGColorRef's (CGColor) as needed (see sample usage). Also provided is an example as a Swift extension so that one can apply a gradient to any UIView.

   func configureGradientBackground(colors:CGColorRef...){

        let gradient: CAGradientLayer = CAGradientLayer()
        let maxWidth = max(self.view.bounds.size.height,self.view.bounds.size.width)
        let squareFrame = CGRect(origin: self.view.bounds.origin, size: CGSizeMake(maxWidth, maxWidth))
        gradient.frame = squareFrame

        gradient.colors = colors
        view.layer.insertSublayer(gradient, atIndex: 0)
    }

To use:

in viewDidLoad...

  override func viewDidLoad() {
        super.viewDidLoad()
        configureGradientBackground(UIColor.redColor().CGColor, UIColor.whiteColor().CGColor)
  }

Extension implementation

extension CALayer {


    func configureGradientBackground(colors:CGColorRef...){

        let gradient = CAGradientLayer()

        let maxWidth = max(self.bounds.size.height,self.bounds.size.width)
        let squareFrame = CGRect(origin: self.bounds.origin, size: CGSizeMake(maxWidth, maxWidth))
        gradient.frame = squareFrame

        gradient.colors = colors

        self.insertSublayer(gradient, atIndex: 0)
    }

}

Extension use-case example:

 override func viewDidLoad() {
        super.viewDidLoad()

        self.view.layer.configureGradientBackground(UIColor.purpleColor().CGColor, UIColor.blueColor().CGColor, UIColor.whiteColor().CGColor)
 }

Which means the gradient background can now be applied to any UIControl since all controls are UIViews (or a subclass) and all UIViews have CALayers.

Swift 4

Extension implementation

extension CALayer {
    public func configureGradientBackground(_ colors:CGColor...){

        let gradient = CAGradientLayer()

        let maxWidth = max(self.bounds.size.height,self.bounds.size.width)
        let squareFrame = CGRect(origin: self.bounds.origin, size: CGSize(width: maxWidth, height: maxWidth))
        gradient.frame = squareFrame

        gradient.colors = colors

        self.insertSublayer(gradient, at: 0)
    }    
}

Extension use-case example:

override func viewDidLoad() {
    super.viewDidLoad()

    self.view.layer.configureGradientBackground(UIColor.purple.cgColor, UIColor.blue.cgColor, UIColor.white.cgColor)
}

K
Kendall Helmstetter Gelner

What you are looking for is CAGradientLayer. Every UIView has a layer - into that layer you can add sublayers, just as you can add subviews. One specific type is the CAGradientLayer, where you give it an array of colors to gradiate between.

One example is this simple wrapper for a gradient view:

http://oleb.net/blog/2010/04/obgradientview-a-simple-uiview-wrapper-for-cagradientlayer/

Note that you need to include the QuartZCore framework in order to access all of the layer parts of a UIView.


A
Alexander Volkov

Swift 4:

Shows gradient in IB correctly:

@IBDesignable public class GradientView: UIView {

    override open class var layerClass: AnyClass {
        return CAGradientLayer.classForCoder()
    }

    required public init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configureGradientLayer()
    }

    public override init(frame: CGRect) {
        super.init(frame: frame)
        configureGradientLayer()
    }

    func configureGradientLayer() {
        let gradientLayer = layer as! CAGradientLayer
        gradientLayer.colors = [UIColor(hex: 0x003399).cgColor, UIColor(hex: 0x00297b).cgColor]
    }
}

d
dulgan

Simple swift view based on Yuchen's version

class GradientView: UIView {
    override class func layerClass() -> AnyClass { return CAGradientLayer.self }

    lazy var gradientLayer: CAGradientLayer = {
        return self.layer as! CAGradientLayer
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

}

Then you can use gradientLayer after initialization like this...

someView.gradientLayer.colors = [UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor]

R
Ronaldo Albertini

Its a good idea to call the solutions above to update layer on the

viewDidLayoutSubviews 

to get the views updated correctly


T
Timur Bernikovich

My solution is to create UIView subclass with CAGradientLayer accessible as a readonly property. This will allow you to customize your gradient how you want and you don't need to handle layout changes yourself. Subclass implementation:

@interface GradientView : UIView

@property (nonatomic, readonly) CAGradientLayer *gradientLayer;

@end

@implementation GradientView

+ (Class)layerClass
{
    return [CAGradientLayer class];
}

- (CAGradientLayer *)gradientLayer
{
    return (CAGradientLayer *)self.layer;
}

@end

Usage:

self.iconBackground = [GradientView new];
[self.background addSubview:self.iconBackground];
self.iconBackground.gradientLayer.colors = @[(id)[UIColor blackColor].CGColor, (id)[UIColor whiteColor].CGColor];
self.iconBackground.gradientLayer.startPoint = CGPointMake(1.0f, 1.0f);
self.iconBackground.gradientLayer.endPoint = CGPointMake(0.0f, 0.0f);

A
Azharhussain Shaikh

SWIFT 3

To add a gradient layer on your view

Bind your view outlet @IBOutlet var YOURVIEW : UIView!

Define the CAGradientLayer() var gradient = CAGradientLayer()

Here is the code you have to write in your viewDidLoad YOURVIEW.layoutIfNeeded() gradient.startPoint = CGPoint(x: CGFloat(0), y: CGFloat(1)) gradient.endPoint = CGPoint(x: CGFloat(1), y: CGFloat(0)) gradient.frame = YOURVIEW.bounds gradient.colors = [UIColor.red.cgColor, UIColor.green.cgColor] gradient.colors = [ UIColor(red: 255.0/255.0, green: 56.0/255.0, blue: 224.0/255.0, alpha: 1.0).cgColor,UIColor(red: 86.0/255.0, green: 13.0/255.0, blue: 232.0/255.0, alpha: 1.0).cgColor,UIColor(red: 16.0/255.0, green: 173.0/255.0, blue: 245.0/255.0, alpha: 1.0).cgColor] gradient.locations = [0.0 ,0.6 ,1.0] YOURVIEW.layer.insertSublayer(gradient, at: 0)


I used this approach, changing "topview" to the name of my IBoutlet for my view. I deleted the "SearchView" line.
Good plan - maybe also note that you can use either of the gradient.colors set ups. So, if you comment out the simple gradient.colors using named UI colors, the image is the same, but if you comment out the other one, the color gradient is then relying on the red to green. Users can do either one.
i
iCyberPaul

In Swift 3.1 I have added this extension to UIView

import Foundation
import UIKit
import CoreGraphics


extension UIView {
    func gradientOfView(withColours: UIColor...) {

        var cgColours = [CGColor]()

        for colour in withColours {
            cgColours.append(colour.cgColor)
        }
        let grad = CAGradientLayer()
        grad.frame = self.bounds
        grad.colors = cgColours
        self.layer.insertSublayer(grad, at: 0)
    }
}

which I then call with

    class OverviewVC: UIViewController {

        override func viewDidLoad() {
            super.viewDidLoad()

            self.view.gradientOfView(withColours: UIColor.red,UIColor.green, UIColor.blue)

        }
}

P
Padmaja

I have implemented this in my code.

UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 31.0f)];
view1.backgroundColor = [UIColor clearColor];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view1.bounds;
UIColor *topColor = [UIColor colorWithRed:132.0/255.0 green:222.0/255.0 blue:109.0/255.0 alpha:1.0];
UIColor *bottomColor = [UIColor colorWithRed:31.0/255.0 green:150.0/255.0 blue:99.0/255.0 alpha:1.0];
gradient.colors = [NSArray arrayWithObjects:(id)[topColor CGColor], (id)[bottomColor CGColor], nil];


[view1.layer insertSublayer:gradient atIndex:0];

Now I can see a gradient on my view.


H
Hardik Thakkar

To give gradient color to UIView (swift 4.2)

func makeGradientLayer(`for` object : UIView, startPoint : CGPoint, endPoint : CGPoint, gradientColors : [Any]) -> CAGradientLayer {
        let gradient: CAGradientLayer = CAGradientLayer()
        gradient.colors = gradientColors
        gradient.locations = [0.0 , 1.0]
        gradient.startPoint = startPoint
        gradient.endPoint = endPoint
        gradient.frame = CGRect(x: 0, y: 0, w: object.frame.size.width, h: object.frame.size.height)
        return gradient
    }

How to use

let start : CGPoint = CGPoint(x: 0.0, y: 1.0)
let end : CGPoint = CGPoint(x: 1.0, y: 1.0)

let gradient: CAGradientLayer = makeGradientLayer(for: cell, startPoint: start, endPoint: end, gradientColors: [
                    UIColor(red:0.92, green:0.07, blue:0.4, alpha:1).cgColor,
                    UIColor(red:0.93, green:0.11, blue:0.14, alpha:1).cgColor
                    ])

self.vwTemp.layer.insertSublayer(gradient, at: 0)

W
Wo_0NDeR ᵀᴹ

This framework is good for gradients and other UI modifications in Historyboard: Design and prototype customized UI, interaction, navigation, transition and animation for App Store ready Apps in Interface Builder with IBAnimatable.

With this you can select a view, set the class to AnimatableView, and from Interface Builder property set gradients and preview results in realtime.

See this to know how apply gradients to UIView.