在Swift中设置按钮上的背景渐变

时间:2016-06-19 01:22:17

标签: ios swift button linear-gradients

我不知道如何在按钮上设置背景渐变(不将背景渐变设置为图像)。这与Android有很大不同。

这是我必须定义一个可退回的梯度方案的类:

import UIKit

extension CAGradientLayer {

    func backgroundGradientColor() -> CAGradientLayer {
        let topColor = UIColor(red: (0/255.0), green: (153/255.0), blue:(51/255.0), alpha: 1)
        let bottomColor = UIColor(red: (0/255.0), green: (153/255.0), blue:(255/255.0), alpha: 1)

        let gradientColors: [CGColor] = [topColor.CGColor, bottomColor.CGColor]
        let gradientLocations: [Float] = [0.0, 1.0]

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

        return gradientLayer

    }
}

我可以使用它来设置我的整个视图的背景:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let background = CAGradientLayer().backgroundGradientColor()
        background.frame = self.view.bounds
        self.view.layer.insertSublayer(background, atIndex: 0)
    }
    //...
}

但是如何访问按钮的视图并插入子图层或类似的东西?我用谷歌搜索了几个小时,找不到任何有用的东西。我很失落。

13 个答案:

答案 0 :(得分:45)

您的代码运行正常。您只需要记住每次都设置渐变的帧。最好只让渐变类别为你设置视图的框架。

这样你就不会忘记并且适用得很好。

extension UIView {
    func applyGradient(colours: [UIColor]) -> Void {
        self.applyGradient(colours, locations: nil)
    }

    func applyGradient(colours: [UIColor], locations: [NSNumber]?) -> Void {
        let gradient: CAGradientLayer = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = colours.map { $0.CGColor }
        gradient.locations = locations
        self.layer.insertSublayer(gradient, atIndex: 0)
    }
}

class ViewController: UIViewController {

    @IBOutlet weak var btn: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        self.btn.titleLabel?.textColor = UIColor.whiteColor()

        self.btn.applyGradient([UIColor.yellowColor(), UIColor.blueColor()])
        self.view.applyGradient([UIColor.yellowColor(), UIColor.blueColor(), UIColor.redColor()], locations: [0.0, 0.5, 1.0])
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

按钮是视图。您将渐变应用于它,就像将其应用于任何其他视图一样。

答案 1 :(得分:14)

下面你可以找到 Swift3 的解决方案(以及 Swift4 )和一点点扩展(方向助手):

typealias GradientPoints = (startPoint: CGPoint, endPoint: CGPoint)

enum GradientOrientation {
    case topRightBottomLeft
    case topLeftBottomRight
    case horizontal
    case vertical

    var startPoint : CGPoint {
        return points.startPoint
    }

    var endPoint : CGPoint {
        return points.endPoint
    }

    var points : GradientPoints {
        switch self {
        case .topRightBottomLeft:
            return (CGPoint(x: 0.0,y: 1.0), CGPoint(x: 1.0,y: 0.0))
        case .topLeftBottomRight:
            return (CGPoint(x: 0.0,y: 0.0), CGPoint(x: 1,y: 1))
        case .horizontal:
            return (CGPoint(x: 0.0,y: 0.5), CGPoint(x: 1.0,y: 0.5))
        case .vertical:
            return (CGPoint(x: 0.0,y: 0.0), CGPoint(x: 0.0,y: 1.0))
        }
    }
}

extension UIView {

    func applyGradient(with colours: [UIColor], locations: [NSNumber]? = nil) {
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = colours.map { $0.cgColor }
        gradient.locations = locations
        self.layer.insertSublayer(gradient, at: 0)
    }

    func applyGradient(with colours: [UIColor], gradient orientation: GradientOrientation) {
        let gradient = CAGradientLayer()
        gradient.frame = self.bounds
        gradient.colors = colours.map { $0.cgColor }
        gradient.startPoint = orientation.startPoint
        gradient.endPoint = orientation.endPoint
        self.layer.insertSublayer(gradient, at: 0)
    }
}

答案 2 :(得分:5)

@Zeb答案很棒,但只是为了清理它并使它更加开心。 计算的只读属性应避免使用get和返回Void是多余的:

typealias GradientPoints = (startPoint: CGPoint, endPoint: CGPoint)

enum GradientOrientation {
  case topRightBottomLeft
  case topLeftBottomRight
  case horizontal
  case vertical

var startPoint: CGPoint {
    return points.startPoint
}

var endPoint: CGPoint {
    return points.endPoint
}

var points: GradientPoints {
    switch self {
    case .topRightBottomLeft:
        return (CGPoint.init(x: 0.0, y: 1.0), CGPoint.init(x: 1.0, y: 0.0))
    case .topLeftBottomRight:
        return (CGPoint.init(x: 0.0, y: 0.0), CGPoint.init(x: 1, y: 1))
    case .horizontal:
        return (CGPoint.init(x: 0.0, y: 0.5), CGPoint.init(x: 1.0, y: 0.5))
    case .vertical:
        return (CGPoint.init(x: 0.0, y: 0.0), CGPoint.init(x: 0.0, y: 1.0))
    }
  }
}

extension UIView {

func applyGradient(withColours colours: [UIColor], locations: [NSNumber]? = nil) {
    let gradient: CAGradientLayer = CAGradientLayer()
    gradient.frame = self.bounds
    gradient.colors = colours.map { $0.cgColor }
    gradient.locations = locations
    self.layer.insertSublayer(gradient, at: 0)
}

func applyGradient(withColours colours: [UIColor], gradientOrientation orientation: GradientOrientation) {
    let gradient: CAGradientLayer = CAGradientLayer()
    gradient.frame = self.bounds
    gradient.colors = colours.map { $0.cgColor }
    gradient.startPoint = orientation.startPoint
    gradient.endPoint = orientation.endPoint
    self.layer.insertSublayer(gradient, at: 0)
  }
}

答案 3 :(得分:2)

为Swift 2.0提供渐变颜色到UIButton

let gradient: CAGradientLayer = CAGradientLayer()

gradient.colors = [(UIColor(red: 59.0/255.0, green: 187.0/255.0, blue: 182.0/255.0, alpha: 1.00).CGColor), (UIColor(red: 57.0/255.0, green: 174.0/255.0, blue: 236.0/255.0, alpha: 1.00).CGColor)]
gradient.locations = [0.0 , 1.0]

gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
gradient.frame = CGRect(x: 0.0, y: 0.0, width: btn.frame.size.width, height: btn.frame.size.height)
btn.layer.insertSublayer(gradient, atIndex: 0)

答案 4 :(得分:1)

我已经尝试了所有这些,这是我在viewdidload中的按钮init

let button = UIButton()
    button.setTitle("Alper", for: .normal)
    button.layer.borderColor = UIColor.white.cgColor
    button.layer.borderWidth = 1
    view.addSubview(button)
    button.anchor(top: nil, left: nil, bottom: logo.topAnchor, right: nil, paddingTop: 0, paddingLeft: 0, paddingBottom: 0, paddingRight: 0, height: 50, width: 100)
    let gradientx = CAGradientLayer()
    gradientx.colors = [UIColor.blue,UIColor.red]
    gradientx.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradientx.endPoint = CGPoint(x: 1.0, y: 1.0)
    gradientx.frame = button.bounds
    button.layer.insertSublayer(gradientx, at: 0)

锚是一个扩展,所以这是无关的渐变。

答案 5 :(得分:1)

试试这个对我有用,

     let button = UIButton(frame: CGRect(x: 60, y: 150, width: 200, height: 60))
    button.setTitle("Email", for: .normal)
    button.backgroundColor = .red
    button.setTitleColor(UIColor.black, for: .normal)
    button.addTarget(self, action: #selector(self.buttonTapped), for: .touchUpInside)

    // Apply Gradient Color
    let gradientLayer:CAGradientLayer = CAGradientLayer()
    gradientLayer.frame.size = button.frame.size
    gradientLayer.colors =
        [UIColor.white.cgColor,UIColor.green.withAlphaComponent(1).cgColor]
    //Use diffrent colors
    button.layer.addSublayer(gradientLayer)
    self.view.addSubview(button)

enter image description here

您可以添加渐变颜色的起点和终点。

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

enter image description here

有关更多详细信息,请参见CAGradientLayer doc

答案 6 :(得分:1)

已经有很多答案了,我想补充一下我为达到这个目的所做的事情。我使用此自定义按钮GradientButton

import Foundation
import UIKit

class GradientButton: UIButton {

    let gradientColors : [UIColor]
    let startPoint : CGPoint
    let endPoint : CGPoint

    required init(gradientColors: [UIColor] = [UIColor.red, UIColor.blue],
                  startPoint: CGPoint = CGPoint(x: 0, y: 0.5),
                  endPoint: CGPoint = CGPoint(x: 1, y: 0.5)) {
        self.gradientColors = gradientColors
        self.startPoint = startPoint
        self.endPoint = endPoint

        super.init(frame: .zero)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()

        let halfOfButtonHeight = layer.frame.height / 2
        contentEdgeInsets = UIEdgeInsets(top: 10, left: halfOfButtonHeight, bottom: 10, right: halfOfButtonHeight)

        layer.anchorPoint = CGPoint(x: 0.5, y: 0.5)

        backgroundColor = UIColor.clear

        // setup gradient

        let gradient = CAGradientLayer()
        gradient.frame = bounds
        gradient.colors = gradientColors.map { $0.cgColor }
        gradient.startPoint = startPoint
        gradient.endPoint = endPoint
        gradient.cornerRadius = 4

        // replace gradient as needed
        if let oldGradient = layer.sublayers?[0] as? CAGradientLayer {
            layer.replaceSublayer(oldGradient, with: gradient)
        } else {
            layer.insertSublayer(gradient, below: nil)
        }

        // setup shadow

        layer.shadowColor = UIColor.darkGray.cgColor
        layer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: halfOfButtonHeight).cgPath
        layer.shadowOffset = CGSize(width: 0.0, height: 1.0)
        layer.shadowOpacity = 0.85
        layer.shadowRadius = 4.0
    }

    override var isHighlighted: Bool {
        didSet {
            let newOpacity : Float = isHighlighted ? 0.6 : 0.85
            let newRadius : CGFloat = isHighlighted ? 6.0 : 4.0

            let shadowOpacityAnimation = CABasicAnimation()
            shadowOpacityAnimation.keyPath = "shadowOpacity"
            shadowOpacityAnimation.fromValue = layer.shadowOpacity
            shadowOpacityAnimation.toValue = newOpacity
            shadowOpacityAnimation.duration = 0.1

            let shadowRadiusAnimation = CABasicAnimation()
            shadowRadiusAnimation.keyPath = "shadowRadius"
            shadowRadiusAnimation.fromValue = layer.shadowRadius
            shadowRadiusAnimation.toValue = newRadius
            shadowRadiusAnimation.duration = 0.1

            layer.add(shadowOpacityAnimation, forKey: "shadowOpacity")
            layer.add(shadowRadiusAnimation, forKey: "shadowRadius")

            layer.shadowOpacity = newOpacity
            layer.shadowRadius = newRadius

            let xScale : CGFloat = isHighlighted ? 1.025 : 1.0
            let yScale : CGFloat = isHighlighted ? 1.05 : 1.0
            UIView.animate(withDuration: 0.1) {
                let transformation = CGAffineTransform(scaleX: xScale, y: yScale)
                self.transform = transformation
            }
        }
    }
}

您可以像这样创建GradientButton实例。

let button = GradientButton.init(gradientColors:[UIColor.black, UIColor.white], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0, y: 1))

答案 7 :(得分:1)

enter image description here

   class ButtonGradient : UIButton {
        override func layoutSubviews() {

            let layer : CAGradientLayer = CAGradientLayer()
            layer.frame.size = self.frame.size
            layer.frame.origin = CGPoint(x: 0, y: 0)

            //   layer.cornerRadius = CGFloat(frame.width / 20)
            let color0 = UIColor(red:255/255, green:122/255, blue:0/255, alpha:1.0).cgColor
            let color1 = UIColor(red:255/255, green:176/255, blue: 0/255, alpha:1.0).cgColor
            let color2 = UIColor(red:250/255, green:98/255, blue: 44/255, alpha:1.0).cgColor
            layer.locations = [0.5, 1.0]
            layer.startPoint = CGPoint(x: 0.0, y: 0.5)
            layer.endPoint = CGPoint(x: 0.5, y: 0.5)
            layer.colors = [color2,color0,color1]

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

之后,直接将“ ButtonGredient”类分配给Storyboard中的特定按钮。

答案 8 :(得分:1)

如果您想在按钮上设置渐变背景,而不是将渐变添加为子图层并在 frame 中更改其 layoutSubviews,而是仅指定按钮的 layerClass是一个CAGradientLayer,所以主层一个渐变:

@IBDesignable
public class GradientButton: UIButton {
    public override class var layerClass: AnyClass         { CAGradientLayer.self }
    private var gradientLayer: CAGradientLayer             { layer as! CAGradientLayer }

    @IBInspectable public var startColor: UIColor = .white { didSet { updateColors() } }
    @IBInspectable public var endColor: UIColor = .red     { didSet { updateColors() } }

    // expose startPoint and endPoint to IB

    @IBInspectable public var startPoint: CGPoint {
        get { gradientLayer.startPoint }
        set { gradientLayer.startPoint = newValue }
    }

    @IBInspectable public var endPoint: CGPoint {
        get { gradientLayer.endPoint }
        set { gradientLayer.endPoint = newValue }
    }

    // while we're at it, let's expose a few more layer properties so we can easily adjust them in IB

    @IBInspectable public var cornerRadius: CGFloat {
        get { layer.cornerRadius }
        set { layer.cornerRadius = newValue }
    }

    @IBInspectable public var borderWidth: CGFloat {
        get { layer.borderWidth }
        set { layer.borderWidth = newValue }
    }

    @IBInspectable public var borderColor: UIColor? {
        get { layer.borderColor.flatMap { UIColor(cgColor: $0) } }
        set { layer.borderColor = newValue?.cgColor }
    }

    // init methods

    public override init(frame: CGRect = .zero) {
        super.init(frame: frame)
        updateColors()
    }

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

private extension GradientButton {
    func updateColors() {
        gradientLayer.colors = [startColor.cgColor, endColor.cgColor]
    }
}

通过设置layerClass,它只会使主图层成为渐变,它会自动为您调整到按钮的bounds。这有一个优点,如果您为按钮大小的变化设置动画(例如,在旋转事件或其他事件上),渐变也将正确设置动画。

而且,这不是必须的,但是把这个类做成@IBDesignable可能会很方便,这样就可以在IB中设置它的属性,它会在storyboard/NIB中正确呈现,不需要额外的代码在视图控制器中。比如我可以在IB中自定义边角、边框、渐变颜色和方向:

enter image description here

答案 9 :(得分:0)

在这里,我已经使用了一个UIView并在其中添加了按钮。

     @IBOutlet weak var btnCenter: UIButton!
     @IBOutlet weak var viewCenter: UIView!

    // Create a gradient layer
    let gradient = CAGradientLayer()

    // gradient colors in order which they will visually appear
    gradient.colors = [UIColor.yello.cgColor, UIColor.blue.cgColor]

    // Gradient from left to right
    gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
    gradient.endPoint = CGPoint(x: 1.0, y: 0.5)

    // set the gradient layer to the same size as the view
    gradient.frame = viewCenter.bounds

    // add the gradient layer to the views layer for rendering
    viewCenter.layer.insertSublayer(gradient, at: 0)

    // Tha magic! Set the button as the views mask
    viewCenter.mask = btnCenter

    //Set corner Radius and border Width of button
    btnCenter.layer.cornerRadius =  btnCenter.frame.size.height / 2
    btnCenter.layer.borderWidth = 5.0

enter image description here

答案 10 :(得分:0)

就这么简单:

import UIKit
class ActualGradientButton: UIButton {

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

    private lazy var gradientLayer: CAGradientLayer = {
        let l = CAGradientLayer()
        l.frame = self.bounds
        l.colors = [UIColor.systemYellow.cgColor, UIColor.systemPink.cgColor]
        l.startPoint = CGPoint(x: 0, y: 0.5)
        l.endPoint = CGPoint(x: 1, y: 0.5)
        l.cornerRadius = 16
        layer.insertSublayer(l, at: 0)
        return l
    }()
}

答案 11 :(得分:0)

我修改了 this great answer 以通过为颜色、半径和渐变方向添加 init 参数来提高按钮的可重用性。

我还添加了 updateGradientColors 方法,因为如果您想在某个时候更改渐变颜色,它可能会很有用。

class GradientButton: UIButton {

    private let colors: [UIColor]
    private let cornerRadius: CGFloat
    private let startPoint: CGPoint
    private let endPoint: CGPoint

    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

    init(colors: [UIColor],
         cornerRadius: CGFloat = 10,
         startPoint: CGPoint = CGPoint(x: 0, y: 0.5),
         endPoint: CGPoint = CGPoint(x: 1, y: 0.5)) {
        self.colors = colors
        self.cornerRadius = cornerRadius
        self.startPoint = startPoint
        self.endPoint = endPoint
        super.init(frame: .zero)
    }

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

    private lazy var gradientLayer: CAGradientLayer = {
        let gl = CAGradientLayer()
        gl.frame = self.bounds
        gl.colors = colors.map { $0.cgColor }
        gl.startPoint = startPoint
        gl.endPoint = endPoint
        gl.cornerRadius = cornerRadius
        layer.insertSublayer(gl, at: 0)
        return gl
    }()

    func updateGradientColors(_ colors: [UIColor]) {
        gradientLayer.colors = colors.map { $0.cgColor }
    }
}

答案 12 :(得分:0)

有一些方法可以在不制作子图层的情况下使用初始图层。

import UIKit
@IBDesignable class GradientButton: UIButton {
    @IBInspectable var startColor: UIColor = UIColor.white
    @IBInspectable var endColor: UIColor = UIColor.white
    @IBInspectable var cornerRadius = CGFloat(5.0)

    override class var layerClass: AnyClass {
        return CAGradientLayer.self
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        //This is an advanced gradient we do not use for now
//        (layer as! CAGradientLayer).startPoint = CGPoint(x: 0, y: 0)
//        (layer as! CAGradientLayer).endPoint = CGPoint(x: 1, y: 1)
//        (layer as! CAGradientLayer).locations = [0,1]

        // Simple gradient
        (layer as! CAGradientLayer).colors = [startColor.cgColor, endColor.cgColor]
        layer.cornerRadius = cornerRadius
    } 
}
相关问题