使用编程方式创建具有颜色渐变的UIView

342
我想在运行时生成背景为渐变颜色(从实色到透明)的视图。有没有办法做到这一点?

2
这是一个非常有用的小工具,可以为您创建渐变代码 https://itunes.apple.com/gb/app/gradient-creator/id1031070259?mt=12 - burrGGG
使用CAGradientLayer创建渐变颜色 https://www.appcoda.com/cagradientlayer/ - Max
22个回答

744

Objective-C:

基础编程语言,用于开发Mac OS X和iOS操作系统上的应用程序。
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:

Swift是一种现代化的编程语言,由苹果公司于2014年发布。它旨在为开发人员提供更快、更安全和更容易维护的编码方式,并且可以与Objective-C代码无缝集成。Swift是一种开源语言,适用于iOS、macOS、watchOS和tvOS等多个苹果平台。
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)

信息: 使用 startPoint 和 endPoint 来改变渐变的方向

如果在这个UIView上添加了任何其他视图(如UILabel),您可能需要考虑将这些视图的背景颜色设置为[UIColor clearColor],以便呈现渐变视图而不是子视图的背景颜色。使用clearColor会带来轻微的性能影响。


14
在所有解决方案中,代码量最短的+1。我只是将其添加到现有的视图控制器中,将2个引用从“view”更改为“self.view”,然后它就可以完美运行了 :) - Ergin
4
同样重要的是在哪里添加这段代码。我需要参考以下这篇帖子才能使其正常工作:https://dev59.com/omUp5IYBdhLWcg3wCD74#20059268 - Garrett Disco
14
这对我帮助很大!其他地方都错过了CGColor部分!谢谢。 - Gyfis
54
不要忘记使用“startPoint”和“endPoint”来改变渐变方向。默认值为(0.5,0)和(0.5,1)-从上到下的方向。 - Valentin Shamardin
15
当我的UIView使用AutoLayout时,在viewDidAppear()didRotateFromInterfaceOrientation()中,我还必须调用gradient.frame = view.bounds,否则渐变将无法正确调整大小。 - EricRobertBrewer
显示剩余6条评论

102

你可以创建一个自定义类 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]
    }
}

在故事板中,将类类型设置为任何您想要具有渐变背景的视图:

输入图像描述

以下是更好的方式:

  • 无需设置CLayer的框架
  • 像往常一样在UIView上使用NSConstraint
  • 不需要创建子层(内存使用较少)

1
在Swift 3中,layerClass不再是函数,而是属性,因此您必须像覆盖属性一样重写它:https://dev59.com/TV4b5IYBdhLWcg3wsDl3。 另外,您还必须实现override init(frame: CGRect),请查阅此答案:https://dev59.com/qV4c5IYBdhLWcg3w7994。 - LaloLoop
你好 @LaloLoop,感谢评论!我已经更新了代码。虽然我不认为我需要重写 init(frame:CGRect)。但我测试了更新后的代码,它可以正常工作。 - Yuchen
1
最佳解决方案! - Baran Emre
@AlexanderVolkov 很不幸地,它没有很好的记录,但 @IBDesignable 类必须实现 init(frame:),否则它们将在 IB 中崩溃。 - idrougge
@idrougge 这不是真的。 - Alexander Volkov
显示剩余2条评论

51

试一下,这对我非常有效:

Objective C

我已经将 RGB 渐变背景色设置给了 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];

enter image description here

更新:- Swift3 +

代码:

 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)

输入图像描述

您可以添加渐变颜色的起始点和结束点。

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

输入图像描述

更多详细描述请参阅 CAGradientLayer文档

希望这能对某些人有所帮助。


36

这是我的推荐方法。

为了促进重用性,我建议创建一个 CAGradientLayer 类别,并将所需的渐变作为类方法添加进去。像这样在 header 文件中指定它们:

#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

然后在你的实现文件中,使用以下语法指定每个梯度:

+ (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;
}

然后只需在您的ViewController或任何其他所需的subclass中导入此类别,然后像这样使用它:

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

你应该把代码放在哪里才能使用它?哪个生命周期方法?viewdidappear会使它出现晚一秒。viewdidload和viewdidappear会导致旋转时无法正常工作。viewdidlayoutsubviews对于旋转效果更好,但仍然会看起来有些不流畅。 - Adam Johns
我个人将其放在 viewDidLoad 中。 - Sam Fischer
1
当我将它放在 viewDidLoad 中时,如果应用程序在横向方向启动,则无法正常工作。我的解决方案是使背景层框架比 view.frame 更宽,以补偿其他方向的问题。 - Adam Johns
2
viewDidLoad函数是有效的,你只需要使用[backgroundLayer setAutoresizingMask:UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight];来设置AutoResizingMask即可。 - ekscrypto

30
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)
    }

}

使用方法

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

21

我只需要在整个应用程序中使用一种类型的渐变,因此我创建了一个UIView的子类,并在初始化时预先配置了渐变层的固定颜色。 UIView的初始化器调用configureGradientLayer方法,该方法配置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

方法名initGradientLayer类似于初始化器。我认为这不是很合适。 - DawnSong
@DawnSong 很好的建议,我已将其更改为“configureGradientLayer”。 - blauzahn

16

我使用Swift的扩展功能以及枚举对已接受的答案进行了一些扩展。

哦,如果你像我一样使用Storyboard,请确保在viewDidLayoutSubviews()或更晚的时间调用gradientBackground(from:to:direction:)

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)
    }
}

这适用于每个解决方案,但只是一个提醒,self.clipsToBounds需要为true或者gradient.cornerRadius需要设置为self.layer.cornerRadius,如果self有圆角。 - Gary Z

12

Swift 实现:

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)

10

我用Swift编写了一个扩展(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)
    }
}

现在我可以像这样在每个视图上设置渐变:

myImageView.addGradientWithColor(UIColor.blue)

Swift3调用应该像这样:myImageView.addGradientWithColor(color: UIColor.blue) - mgyky

8

Swift实现

这个答案是在前面的答案基础之上提供了解决渐变在旋转时没有正确应用的问题的实现方法。它通过将渐变层更改为正方形来解决这个问题,从而使所有方向的旋转都产生正确的渐变效果。函数签名包括一个Swift可变参数,允许传递尽可能多的CGColorRef(CGColor),如需要(请参见示例用法)。还提供了一个Swift扩展的示例,以便将渐变应用于任何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)
    }

使用方法:

在viewDidLoad中...

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

扩展实现

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)
    }

}

扩展使用案例示例:

 override func viewDidLoad() {
        super.viewDidLoad()

        self.view.layer.configureGradientBackground(UIColor.purpleColor().CGColor, UIColor.blueColor().CGColor, UIColor.whiteColor().CGColor)
 }
这意味着渐变背景现在可以应用到任何UIControl上,因为所有控件都是UIView(或其子类),而所有UIView都有CALayer。 Swift 4 扩展实现
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)
    }    
}

扩展使用案例示例:

override func viewDidLoad() {
    super.viewDidLoad()

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,