【问题标题】:Radial gradient background in SwiftSwift 中的径向渐变背景
【发布时间】:2015-08-06 10:51:27
【问题描述】:

我一直在尝试制作基本的径向渐变背景,但没有成功。我设法得到一个线性渐变,如下面的代码所示,但我不知道如何用不同的颜色使它呈放射状 - 如下图所示。任何帮助将不胜感激。 :)

    let gradientLayer: CAGradientLayer = CAGradientLayer()
    gradientLayer.colors = gradientColors
    gradientLayer.locations = gradientLocations ...

【问题讨论】:

  • 如果你这样做是为了学习,那很好,但如果你是为了设计目的,我建议你在sketch3中设计它并将它用作UIImage
  • 这个*.com/questions/16043534/… 可以帮助你。
  • @sriramhegde 背景需要动态变化。一个图像不会有帮助。谢谢。
  • 请注意这个老问题,现在很容易 - 请在下面回答!
  • (显然永远不要在 iPhone 应用程序中使用图像作为渐变!GPU 非常擅长制作渐变;图像看起来很糟糕,不会缩放,而且性能非常差。)

标签: ios swift


【解决方案1】:

如今 CAGradientLayer 已内置于 iOS。

就这么简单:

多年来,您只需这样做:

class GlowBall: UIView {
    private lazy var pulse: CAGradientLayer = {
        let l = CAGradientLayer()
        l.type = .radial
        l.colors = [ UIColor.red.cgColor,
            UIColor.yellow.cgColor,
            UIColor.green.cgColor,
            UIColor.blue.cgColor]
        l.locations = [ 0, 0.3, 0.7, 1 ]
        l.startPoint = CGPoint(x: 0.5, y: 0.5)
        l.endPoint = CGPoint(x: 1, y: 1)
        layer.addSublayer(l)
        return l
    }()

    override func layoutSubviews() {
        super.layoutSubviews()
        pulse.frame = bounds
        pulse.cornerRadius = bounds.width / 2.0
    }

}
    

重点是:

l.colors = [ UIColor.red.cgColor,
                UIColor.yellow.cgColor,
                UIColor.green.cgColor,
                UIColor.blue.cgColor]
l.locations = [ 0, 0.3, 0.7, 1 ]
    

请注意,您可以根据需要更改“拉伸”...

l.locations = [ 0, 0.1, 0.2, 1 ]
    
    

使用任何你喜欢的颜色

l.colors = [ UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor,
                    UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor,
                    UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor,
                    UIColor.systemBlue.cgColor,
                    UIColor.systemPink.cgColor]
                l.locations = [ 0,0.1,0.2,0.3,0.4,0.5,0.6,1 ]
    

现在真的那么容易。

非常有用的技巧:

假设您想要黄色,在 0.6 处有一个蓝色带

l.colors = [ UIColor.yellow.cgColor,
                    UIColor.blue.cgColor,
                    UIColor.yellow.cgColor]
                l.locations = [ 0, 0.6, 1 ]
    

效果很好。

    # yellow...
    # blue...
    # yellow...
    

但通常你会这样做:

    # yellow...
    # yellow...
    # blue...
    # yellow...
    # yellow...
    

请注意,每端有两个黄色 ...

l.colors = [ UIColor.yellow.cgColor,
 UIColor.yellow.cgColor,
 UIColor.blue.cgColor,
 UIColor.yellow.cgColor,
 UIColor.yellow.cgColor]
    

通过这种方式,您可以控制“多宽”蓝色带的宽度:

在本例中:蓝色带将窄而锐利

l.locations = [ 0, 0.58, 0.6, 0.68, 1 ]
    

在本例中,蓝色带将宽而柔和

l.locations = [ 0, 0.5, 0.6, 0.7, 1 ]
    

这确实是控制渐变并获得所需外观的秘诀。


如何使用...

注意这是 - 非常简单 - 一个 UIView !!

class GlowBall: UIView { ...

就这样

  1. 在情节提要中,将 UIView 放置在您想要的位置

  2. 在故事板中,将类更改为“GlowBall”而不是 UIView

你已经完成了!

【讨论】:

  • 是的,没问题! :) 感谢您的解决方案。
  • 很好的答案。我认为展示如何使用它会很有帮助。
【解决方案2】:

如果您只是在寻找 UIView 径向渐变背景,这里是 Swift 3 中的一个实现:

class RadialGradientLayer: CALayer {

    var center: CGPoint {
        return CGPoint(x: bounds.width/2, y: bounds.height/2)
    }

    var radius: CGFloat {
        return (bounds.width + bounds.height)/2
    }

    var colors: [UIColor] = [UIColor.black, UIColor.lightGray] {
        didSet {
            setNeedsDisplay()
        }
    }

    var cgColors: [CGColor] {
        return colors.map({ (color) -> CGColor in
            return color.cgColor
        })
    }

    override init() {
        super.init()
        needsDisplayOnBoundsChange = true
    }

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

    override func draw(in ctx: CGContext) {
        ctx.saveGState()
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let locations: [CGFloat] = [0.0, 1.0]
        guard let gradient = CGGradient(colorsSpace: colorSpace, colors: cgColors as CFArray, locations: locations) else {
            return
        }
        ctx.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: radius, options: CGGradientDrawingOptions(rawValue: 0))
    }

}



class RadialGradientView: UIView {

    private let gradientLayer = RadialGradientLayer()

    var colors: [UIColor] {
        get {
            return gradientLayer.colors
        }
        set {
            gradientLayer.colors = newValue
        }
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        if gradientLayer.superlayer == nil {
            layer.insertSublayer(gradientLayer, at: 0)
        }
        gradientLayer.frame = bounds
    }

}

【讨论】:

  • 简洁的解决方案,+ 便于扩展。
  • 如何在 Playground 中使用它?
  • 对于在此处搜索的任何人,请注意CAGradientLayer 现在存在。这些天你绝对不需要draw。它是完全内置的。 (作为一个细节,我相信如果发送的数组的数量不是 2 项,这将崩溃。)
【解决方案3】:

看看我的 RadialGradientLayer 实现,并随时修改它

class RadialGradientLayer: CALayer {

   override init(){

        super.init()

        needsDisplayOnBoundsChange = true
    }

     init(center:CGPoint,radius:CGFloat,colors:[CGColor]){

        self.center = center
        self.radius = radius
        self.colors = colors

        super.init()

    }

    required init(coder aDecoder: NSCoder) {

        super.init()

    }

    var center:CGPoint = CGPointMake(50,50)
    var radius:CGFloat = 20
    var colors:[CGColor] = [UIColor(red: 251/255, green: 237/255, blue: 33/255, alpha: 1.0).CGColor , UIColor(red: 251/255, green: 179/255, blue: 108/255, alpha: 1.0).CGColor]

    override func drawInContext(ctx: CGContext!) {

        CGContextSaveGState(ctx)

        var colorSpace = CGColorSpaceCreateDeviceRGB()

        var locations:[CGFloat] = [0.0, 1.0]

        var gradient = CGGradientCreateWithColors(colorSpace, colors, [0.0,1.0])

        var startPoint = CGPointMake(0, self.bounds.height)
        var endPoint = CGPointMake(self.bounds.width, self.bounds.height)

        CGContextDrawRadialGradient(ctx, gradient, center, 0.0, center, radius, 0)

    }

}

在我的情况下,我只需要两种颜色,如果您需要更多颜色,则需要修改 drawInContext 中声明的 location 数组。同样在从这个类创建对象后不要忘记调用它的setNeedsDisplay(),否则它不会工作。有时我需要不同大小的渐变,这就是为什么你必须在初始化程序中传递半径参数和渐变的中心点

【讨论】:

  • 这就是我在 viewDidLoad 中创建对象的方式,但由于某种原因它不起作用。 @zellb。 var screenCentre = CGPointMake(100, 100) 让 innerColour = UIColor.redColor() 为! CGColor 让 outterColour = UIColor.greenColor() 为! CGColor varradialGradientBackground = RadialGradientLayer(中心:screenCentre,半径:CGFloat(50.0),颜色:[innerColour,outterColour])radialGradientBackground.frame = self.view!.bounds self.view!.layer.insertSublayer(radialGradientBackground,atIndex:0) self.view!.setNeedsDisplay()
  • setNeedsDisplay 必须在radialGradientBackground 上调用,而不是在视图中
  • 甜蜜。运作良好@zellb。谢谢。 ;)
  • @Gugulethu 很高兴听到这个消息
  • locations、startPoint 和 endPoint 甚至没有在你的 drawInContext 中使用
【解决方案4】:
@IBDesignable class RadialGradientView: UIView {

    @IBInspectable var outsideColor: UIColor = UIColor.red
    @IBInspectable var insideColor: UIColor = UIColor.green

    override func draw(_ rect: CGRect) {
        let colors = [insideColor.cgColor, outsideColor.cgColor] as CFArray
        let endRadius = sqrt(pow(frame.width/2, 2) + pow(frame.height/2, 2))
        let center = CGPoint(x: bounds.size.width / 2, y: bounds.size.height / 2)
        let gradient = CGGradient(colorsSpace: nil, colors: colors, locations: nil)
        let context = UIGraphicsGetCurrentContext()

        context?.drawRadialGradient(gradient!, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: endRadius, options: CGGradientDrawingOptions.drawsBeforeStartLocation)
    }
}

查看我的完整答案here

【讨论】:

  • CAGradientLayer 现在已经完全内置在 iOS 中,多年来
【解决方案5】:

采用父视图、颜色和位置作为输入的函数有点不同。该函数返回添加图层的子视图。这提供了隐藏/显示/删除子视图的灵活性。

override func viewDidLoad() {
    super.viewDidLoad()
    //squareView is my parent view I am going to add gradient view to it
    squareView.backgroundColor = UIColor.black
    //Add CG colors
    let colours = [UIColor.red.cgColor,UIColor.green.cgColor,UIColor.clear.cgColor]
    //Add location with same count as colors, these describe contribution in gradient from center 0 to end 1
    let locations:[NSNumber] = [0,0.6,0.8]
    //Use gradientView reference to show/hide, remove/re-add from view
    let gradientView = self.addGradientViewTo(parentView: self.squareView, colors:colours,locations: locations)
}

func addGradientViewTo (parentView:UIView,colors:[CGColor],locations:[NSNumber]) -> UIView  {
    //Create customGradientView with exact dimension of parent, add it with centering with parent
    let customGradientView = UIView()
    customGradientView.backgroundColor = UIColor.clear
    customGradientView.frame = parentView.bounds
    parentView.addSubview(customGradientView)
    customGradientView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor).isActive = true
    customGradientView.centerYAnchor.constraint(equalTo: parentView.centerYAnchor).isActive = true
    parentView.clipsToBounds = true

    //Create layer add it to customGradientView
    let gradientLayer = CAGradientLayer()
    gradientLayer.type = .radial //Circular
    gradientLayer.opacity = 0.8
    gradientLayer.colors = colors
    gradientLayer.locations = locations
    gradientLayer.frame = customGradientView.bounds
    
    //Set start point as center and radius as 1, co-ordinate system maps 0 to 1, 0,0 top left, bottom right 1,1
    gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
    let radius = 1.0
    gradientLayer.endPoint = CGPoint(x: radius, y: radius)
          
    //Add layer at top to make sure its visible
    let layerCount:UInt32 = UInt32(customGradientView.layer.sublayers?.count ?? 0)
    customGradientView.layer.insertSublayer(gradientLayer, at: layerCount)
    customGradientView.layoutIfNeeded()
    
    //Use reference to show/hide add/remove gradient view
    return customGradientView
}

【讨论】: