Set Background Gradient on Button in Swift - ios
I have no idea how to set the background gradient on a button (without making the background gradient an image). This is so different from Android.
Here's a class I have to define a returnable gradient scheme:
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
}
}
I can use this to set the background of my entire view with the following:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let background = CAGradientLayer().backgroundGradientColor()
background.frame = self.view.bounds
self.view.layer.insertSublayer(background, atIndex: 0)
}
//...
}
But how can I access the view of the button and insert the sublayer or something like that?
Your code works fine. You just have to remember to set the gradient's frame every time. It is better to just make the gradient category also set the frame of the view for you.
That way you don't forget and it applies fine.
import UIKit
extension UIView {
func applyGradient(colours: [UIColor]) -> CAGradientLayer {
return self.applyGradient(colours: colours, locations: nil)
}
func applyGradient(colours: [UIColor], locations: [NSNumber]?) -> CAGradientLayer {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = colours.map { $0.cgColor }
gradient.locations = locations
self.layer.insertSublayer(gradient, at: 0)
return gradient
}
}
class ViewController: UIViewController {
#IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
self.btn.applyGradient(colours: [.yellow, .blue])
self.view.applyGradient(colours: [.yellow, .blue, .red], locations: [0.0, 0.5, 1.0])
}
}
Buttons are views. You apply gradients to it the same way you would apply it to any other view.
Picture Proof:
Video Proof:
https://i.imgur.com/ssDTqPu.mp4
It's this simple:
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
}()
}
Here below you can find the solution for Swift3 (and Swift4 too) and a little bit extended (orientation helper):
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)
}
}
#Zeb answer is great but just to clean it up and make it a little more swifty.
Computed read-only properties should avoid using get and returning Void is redundant:
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(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)
}
}
If you want a gradient background on a button, rather than adding the gradient as a sublayer and changing its frame in layoutSubviews, I would instead just specify the layerClass of the button to be a CAGradientLayer, so the main layer is a gradient:
#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]
}
}
By setting the layerClass, it will just make the main layer be a gradient, which automatically is adjusted to the bounds of the button for you. This has an advantage that if you animate the changing of the button size (e.g. on rotation events or whatever), the gradient will be correctly animated, too.
And, it is not necessary, but it may be convenient to make this class an #IBDesignable, so one can set its properties in IB, and it will be correctly rendered in the storyboard/NIB with no additional code in the view controller. For example, I can customize the corners, border, and gradient colors and direction in IB:
Try this is working for me ,
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)
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)
For more details description refer CAGradientLayer doc
I have tried all of them this is my button init inside of viewdidload
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)
anchor is an extension, so this is irrelevant gradient.
There are already many answers there I want add what I did to achieve this. I use this custom Button 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
}
}
}
}
You can make GradientButton instance like this.
let button = GradientButton.init(gradientColors:[UIColor.black, UIColor.white], startPoint: CGPoint(x: 0, y: 0), endPoint: CGPoint(x: 0, y: 1))
For Swift
extension UIViewController {
func makeGradientColor(`for` object : AnyObject , startPoint : CGPoint , endPoint : CGPoint) -> CAGradientLayer {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [(UIColor.red.cgColor), (UIColor.yellow.cgColor)]
gradient.locations = [0.0 , 1.0]
gradient.startPoint = startPoint
gradient.endPoint = endPoint
gradient.frame = CGRect(x: 0.0, y: 0.0, width: object.bounds.size.width, height: object.bounds.size.height)
return gradient
}
}
How to use ?
if let layers = btn.layer.sublayers{
for layer in layers {
if layer.isKind(of: CAGradientLayer.self) {
layer.removeFromSuperlayer()
}
}
}
let start : CGPoint = CGPoint(x: 0.0, y: 0.0)
let end : CGPoint = CGPoint(x: 1.0, y: 1.0)
let gradient: CAGradientLayer = self.makeGradientColor(for: cell.bgView, startPoint: start, endPoint: end)
btn.layer.insertSublayer(gradient, at: 0)
I've modified this great answer to improve the reusability of the button by adding init parameters for colors, radius, and gradient direction.
I also added updateGradientColors method as it might be useful if you want to change the gradient color at some point.
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 }
}
}
Gradient Button with corner radius, start and End Points Code is here...
extension UIView {
func applyGradient(colours: [UIColor], cornerRadius: CGFloat?, startPoint: CGPoint, endPoint: CGPoint) {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = self.bounds
if let cornerRadius = cornerRadius {
gradient.cornerRadius = cornerRadius
}
gradient.startPoint = startPoint
gradient.endPoint = endPoint
gradient.colors = colours.map { $0.cgColor }
self.layer.insertSublayer(gradient, at: 0)
}
}
Usage :
self.yourButton.applyGradient(colours: [.red, .green], cornerRadius: 20, startPoint: CGPoint(x: 0, y: 0.5), endPoint: CGPoint(x: 1, y: 0.5))
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)
}
}
After that directly assign "ButtonGredient" class to particular button in Storyboard.
Here, I have taken one UIView and add button in it.
#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
There are ways to work with initial layer without making sublayers.
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
}
}
class GradientButton: UIButton {
var gradientLayer: CAGradientLayer? {
didSet {
layer.sublayers?.filter { $0 is CAGradientLayer }.forEach { $0.removeFromSuperlayer() }
if let gradientLayer = gradientLayer {
layer.insertSublayer(gradientLayer, at: 0)
}
}
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer?.frame = self.bounds
}
}
Related
UIView with gradient not changing colors when button is tapped
When a user clicks on a button, it randomizes the colors used to create a gradient in a large circle. Beneath it are two small circles that display the solid colors used for the gradient. They all display correctly at the start (main circle is a gradient of the randomized smaller circles' color) but when I click on the button, only the smaller circles change color; the large circle stays at the same gradient colors. Extensions and View Controller: extension UIView { func setupGradientBackground(colorOne: UIColor, colorTwo: UIColor) { let gradientLayer: CAGradientLayer = CAGradientLayer() gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor] gradientLayer.locations = [0.0, 1.0] gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) gradientLayer.frame = self.bounds self.layer.insertSublayer(gradientLayer, at: 0) } } extension UIColor { static var random: UIColor { return UIColor(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1), alpha: 1.0) } } class GradientController: UIViewController { let gradientView = GradientView() let leftGradientColor: UIColor = .random let rightGradientColor: UIColor = .random } override func viewDidLoad() { super.viewDidLoad() view = gradientView newGradient() } func newGradient() { gradientView.mainCircleView.setupGradientBackground(colorOne: leftGradientColor, colorTwo: rightGradientColor) gradientView.colorCircleLeftView.backgroundColor = leftGradientColor gradientView.colorCircleRightView.backgroundColor = rightGradientColor gradientView.gradientGenerateButton.addTarget(self, action: #selector(randomGradient(sender:)), for: .touchUpInside) } #objc func randomGradient(sender: UIButton) { let leftGradient = UIColor.random let rightGradient = UIColor.random gradientView.colorCircleLeftView.backgroundColor = leftGradient gradientView.colorCircleRightView.backgroundColor = rightGradient //Here is where it's not changing colors. Doesn't seem like the VC recognizes it in this function gradientView.mainCircleView.setupGradientBackground(colorOne: leftGradient, colorTwo: rightGradient) } View: class GradientView: UIView { //circle's UIView code in Extensions let mainCircleView = UIView().circleView(width: 380, height: 380) let colorCircleLeftView = UIView().circleView(width: 40, height: 40) let colorCircleRightView = UIView().circleView(width: 40, height: 40) ... func setupLayout() { ... } } What I've tried is changing the mainCircleView color to solid UIColors, like gradientView.mainCircleView.setupGradientBackground(colorOne: .red, colorTwo: .orange) to see if the main Circle changes to those colors in both func newGradient() and #objc func randomGradient(sender: UIButton). It only changes in func newGradient() what I've set manually, so that means the VC isn't recognizing the main Circle in the #objc func but I'm lost on how to fix it... Any help is appreciated! What it looks like when I click the "Generate" button (large circle should be showing brown and purple):
Update your function with this, You have to remove old layer and then insert new Sublayer. Solution 1 : func setupGradientBackground(colorOne: UIColor, colorTwo: UIColor) { if let gradientLayer = (self.layer.sublayers?.compactMap { $0 as? CAGradientLayer })?.first { gradientLayer.removeFromSuperlayer() } let gradientLayer: CAGradientLayer = CAGradientLayer() gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor] gradientLayer.locations = [0.0, 1.0] gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) gradientLayer.frame = self.bounds self.layer.insertSublayer(gradientLayer, at: 0) } Solution 2 : func setupGradientBackground(colorOne: UIColor, colorTwo: UIColor) { let gradientLayer = layer.sublayers?.first as? CAGradientLayer ?? CAGradientLayer() gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor] gradientLayer.locations = [0.0, 1.0] gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) gradientLayer.frame = self.bounds guard gradientLayer.superlayer != self else { return } layer.insertSublayer(gradientLayer, at: 0) } Solution 3 : you can set name to your CAGradientLayer, This will help you for removing that particular layer. func setupGradientBackground(colorOne: UIColor, colorTwo: UIColor) { for layer in layer.sublayers ?? [] { if layer.name == "GradientLayer" { layer.removeFromSuperlayer() } } let gradientLayer: CAGradientLayer = CAGradientLayer() gradientLayer.colors = [colorOne.cgColor, colorTwo.cgColor] gradientLayer.locations = [0.0, 1.0] gradientLayer.startPoint = CGPoint(x: 0.0, y: 1.0) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) gradientLayer.name = "GradientLayer" gradientLayer.frame = self.bounds self.layer.insertSublayer(gradientLayer, at: 0) }
The gradient does not change because you add a new gradient below the existing one extension UIView { func setupGradientBackground(colorOne: UIColor, colorTwo: UIColor) { let gradientLayer: CAGradientLayer = CAGradientLayer() ... // ⬇ There is a mistake. self.layer.insertSublayer(gradientLayer, at: 0) } } You should change the current gradient with this new one. Or insert a new one above the old one.
Changing the gradient layer colors after setting it extension CALayer { func updateGradientColors(_ colors:CGColor...) { if let layer = sublayers?.first as? CAGradientLayer { let frame = CGRect(origin: self.bounds.origin, size: CGSize(width: self.bounds.size.width, height: self.bounds.size.height)) layer.frame = frame layer.colors = colors } } }
How to create a rotating rainbow color circle in iOS
From stackoverflow i got a code for drawing rainbow color circle.But as part of requirement ,I need that circle to be rotated continously ,like a rotating progress loader.Below is the code used for creating Rainbow color circle. class RainbowCircle: UIView { private var radius: CGFloat { return frame.width>frame.height ? frame.height/2 : frame.width/2 } private var stroke: CGFloat = 10 private var padding: CGFloat = 5 //MARK: - Drawing override func draw(_ rect: CGRect) { super.draw(rect) drawRainbowCircle(outerRadius: radius - padding, innerRadius: radius - stroke - padding, resolution: 1) } init(frame: CGRect, lineHeight: CGFloat) { super.init(frame: frame) stroke = lineHeight } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } /* Resolution should be between 0.1 and 1 */ private func drawRainbowCircle(outerRadius: CGFloat, innerRadius: CGFloat, resolution: Float) { guard let context = UIGraphicsGetCurrentContext() else { return } context.saveGState() context.translateBy(x: self.bounds.midX, y: self.bounds.midY) //Move context to center let subdivisions:CGFloat = CGFloat(resolution * 512) //Max subdivisions of 512 let innerHeight = (CGFloat.pi*innerRadius)/subdivisions //height of the inner wall for each segment let outterHeight = (CGFloat.pi*outerRadius)/subdivisions let segment = UIBezierPath() segment.move(to: CGPoint(x: innerRadius, y: -innerHeight/2)) segment.addLine(to: CGPoint(x: innerRadius, y: innerHeight/2)) segment.addLine(to: CGPoint(x: outerRadius, y: outterHeight/2)) segment.addLine(to: CGPoint(x: outerRadius, y: -outterHeight/2)) segment.close() //Draw each segment and rotate around the center for i in 0 ..< Int(ceil(subdivisions)) { UIColor(hue: CGFloat(i)/subdivisions, saturation: 1, brightness: 1, alpha: 1).set() segment.fill() //let lineTailSpace = CGFloat.pi*2*outerRadius/subdivisions //The amount of space between the tails of each segment let lineTailSpace = CGFloat.pi*2*outerRadius/subdivisions segment.lineWidth = lineTailSpace //allows for seemless scaling segment.stroke() // //Rotate to correct location let rotate = CGAffineTransform(rotationAngle: -(CGFloat.pi*2/subdivisions)) //rotates each segment segment.apply(rotate) } Please anyone help me in rotating this circle. Please find below the circle generated with above code:
What you got looks completely overcomplicated in the first place. Take a look at the following example: class ViewController: UIViewController { class RainbowView: UIView { var segmentCount: Int = 10 { didSet { refresh() } } var lineWidth: CGFloat = 10 { didSet { refresh() } } override var frame: CGRect { didSet { refresh() } } override func layoutSubviews() { super.layoutSubviews() refresh() } private var currentGradientLayer: CAGradientLayer? private func refresh() { currentGradientLayer?.removeFromSuperlayer() guard segmentCount > 0 else { return } currentGradientLayer = { let gradientLayer = CAGradientLayer() gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5) gradientLayer.endPoint = CGPoint(x: 0.5, y: 0) gradientLayer.type = .conic let colors: [UIColor] = { var colors: [UIColor] = [UIColor]() for i in 0..<segmentCount { colors.append(UIColor(hue: CGFloat(i)/CGFloat(segmentCount), saturation: 1, brightness: 1, alpha: 1)) } colors.append(UIColor(hue: 0.0, saturation: 1, brightness: 1, alpha: 1)) // Append start color at the end as well to complete the circle return colors; }() gradientLayer.colors = colors.map { $0.cgColor } gradientLayer.frame = bounds layer.addSublayer(gradientLayer) gradientLayer.mask = { let shapeLayer = CAShapeLayer() shapeLayer.frame = bounds shapeLayer.lineWidth = lineWidth shapeLayer.strokeColor = UIColor.white.cgColor shapeLayer.fillColor = UIColor.clear.cgColor shapeLayer.path = UIBezierPath(ovalIn: bounds.inset(by: UIEdgeInsets(top: lineWidth*0.5, left: lineWidth*0.5, bottom: lineWidth*0.5, right: lineWidth*0.5))).cgPath return shapeLayer }() return gradientLayer }() } } override func viewDidLoad() { super.viewDidLoad() view.addSubview({ let view = RainbowView(frame: CGRect(x: 50.0, y: 100.0, width: 100.0, height: 100.0)) var angle: CGFloat = 0.0 Timer.scheduledTimer(withTimeInterval: 1.0/60.0, repeats: true, block: { _ in angle += 0.01 view.transform = CGAffineTransform(rotationAngle: angle) }) return view }()) } } So a view is generated that uses a conical gradient with mask to draw the circle you are describing. Then a transform is applied to the view to rotate it. And a Timer is scheduled to rotate the circle. Note that this code will leak because timer is nowhere invalidated. It needs to be removed when view disappears or similar.
The easiest way would be to attach an animation that repeats forever: let animation = CABasicAnimation(keyPath: "transform.rotation") // Create rotation animation animation.repeatCount = .greatestFiniteMagnitude // Repeat animation for as long as we can animation.fromValue = 0 // Rotate from 0 animation.toValue = 2 * Float.pi // to 360 deg animation.duration = 1 // During 1 second self.layer.add(animation, forKey: "animation") // Adding the animation to the view self - is RainbowCircle, assuming that you add this code to one of the methods inside it.
For this we can have Image something like this syncImage.image = UIImage(named:"spinning") Create a below extension to Start/Stop Rotating extension UIView { // To animate func startRotating(duration: Double = 1) { let kAnimationKey = "rotation" if self.layer.animationForKey(kAnimationKey) == nil { let animate = CABasicAnimation(keyPath: "transform.rotation") animate.duration = duration animate.repeatCount = Float.infinity animate.fromValue = 0.0 animate.toValue = Float(M_PI * 2.0) self.layer.addAnimation(animate, forKey: kAnimationKey) } } func stopRotating() { let kAnimationKey = "rotation" if self.layer.animationForKey(kAnimationKey) != nil { self.layer.removeAnimationForKey(kAnimationKey) } } } Usage func startSpinning() { syncImage.startRotating() } func stopSpinning() { syncImage.stopRotating() } func handleSyncTap(sender: UITapGestureRecognizer? = nil) { startSpinning() let dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(3 * Double(NSEC_PER_SEC))) dispatch_after(dispatchTime, dispatch_get_main_queue(), { self.stopSpinning() }) }
Gradient layer is not working in iOS
I have the code for making gradient but it's simply not showing. If I change the color of view that holds that gradient, I can see it. So the view is fine, just gradient has some issues. This is my code: class KolodaCardView: UIView { var helloWorld = "Hello World" var userImage = UIImageView() var userName = UILabel() var parent = UIView() var gradient = CAGradientLayer() var gradientView = UIView() override init(frame: CGRect) { super.init(frame: frame) parent = self parent.backgroundColor = .clear parent.layer.cornerRadius = 16 parent.clipsToBounds = true setupUserImage() setupUserName() } override func layoutSubviews() { super.layoutSubviews() gradient.frame = gradientView.frame } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } func setupUserImage() { parent.addSubview(userImage) userImage.snp.makeConstraints { make in make.top.equalTo(parent) make.bottom.equalTo(parent) make.left.equalTo(parent) make.right.equalTo(parent) } userImage.contentMode = .scaleAspectFill userImage.clipsToBounds = true userImage.layer.cornerRadius = 16 userImage.addSubview(gradientView) gradientView.snp.makeConstraints { (make) in make.bottom.equalToSuperview() make.left.equalToSuperview() make.right.equalToSuperview() make.height.equalTo(60) } // gradientView.backgroundColor = .green gradient.startPoint = CGPoint(x: 0, y: 0) gradient.endPoint = CGPoint(x: 0, y: 1) gradient.locations = [0.5,1.0] gradient.frame = gradientView.bounds gradient.colors = [UIColor.red.cgColor, UIColor.yellow.cgColor] gradient.startPoint = CGPoint(x: 0.0, y: 1.0) gradient.endPoint = CGPoint(x: 1.0, y: 1.0) gradientView.layer.insertSublayer(gradient, at: 0) } func setupUserName() { parent.addSubview(userName) userName.snp.makeConstraints { (make) in make.left.equalTo(parent).offset(16) make.right.equalTo(parent) make.height.equalTo(20) make.bottom.equalTo(-20) } userName.textColor = .black userName.textAlignment = .left } } I checked several solutions here but nothing works! Can somebody check if maybe I am overlooking something?
In Gradient you have to give location of your gradient color that fill your layer with their start point to end point. gradientLayer.startPoint = CGPoint(x: 0, y: 0) gradientLayer.endPoint = CGPoint(x: 0, y: 1) gradientLayer.colors = [UIColor.red.cgColor ,UIColor.yellow.cgColor] gradientLayer.locations = [0.5,1.0] your_gradientView.layer.insertSublayer(gradientLayer, at: 0)
How to Apply Gradient to background view of iOS Swift App
I'm trying to apply a gradient as the background color of a View (main view of a storyboard). The code runs, but nothing changes. I'm using xCode Beta 2 and Swift. Here's the code: class Colors { let colorTop = UIColor(red: 192.0/255.0, green: 38.0/255.0, blue: 42.0/255.0, alpha: 1.0) let colorBottom = UIColor(red: 35.0/255.0, green: 2.0/255.0, blue: 2.0/255.0, alpha: 1.0) let gl: CAGradientLayer init() { gl = CAGradientLayer() gl.colors = [ colorTop, colorBottom] gl.locations = [ 0.0, 1.0] } } then in the view controller: let colors = Colors() func refresh() { view.backgroundColor = UIColor.clearColor() var backgroundLayer = colors.gl backgroundLayer.frame = view.frame view.layer.insertSublayer(backgroundLayer, atIndex: 0) } } }
Xcode 11 • Swift 5.1 You can design your own Gradient View as follow: #IBDesignable public class Gradient: UIView { #IBInspectable var startColor: UIColor = .black { didSet { updateColors() }} #IBInspectable var endColor: UIColor = .white { didSet { updateColors() }} #IBInspectable var startLocation: Double = 0.05 { didSet { updateLocations() }} #IBInspectable var endLocation: Double = 0.95 { didSet { updateLocations() }} #IBInspectable var horizontalMode: Bool = false { didSet { updatePoints() }} #IBInspectable var diagonalMode: Bool = false { didSet { updatePoints() }} override public class var layerClass: AnyClass { CAGradientLayer.self } var gradientLayer: CAGradientLayer { layer as! CAGradientLayer } func updatePoints() { if horizontalMode { gradientLayer.startPoint = diagonalMode ? .init(x: 1, y: 0) : .init(x: 0, y: 0.5) gradientLayer.endPoint = diagonalMode ? .init(x: 0, y: 1) : .init(x: 1, y: 0.5) } else { gradientLayer.startPoint = diagonalMode ? .init(x: 0, y: 0) : .init(x: 0.5, y: 0) gradientLayer.endPoint = diagonalMode ? .init(x: 1, y: 1) : .init(x: 0.5, y: 1) } } func updateLocations() { gradientLayer.locations = [startLocation as NSNumber, endLocation as NSNumber] } func updateColors() { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } override public func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) updatePoints() updateLocations() updateColors() } }
The Colors you're providing to gradient must be of type CGColor. So set your array of CGColor to gl.colors. The correct code is : class Colors { var gl:CAGradientLayer! init() { let colorTop = UIColor(red: 192.0 / 255.0, green: 38.0 / 255.0, blue: 42.0 / 255.0, alpha: 1.0).cgColor let colorBottom = UIColor(red: 35.0 / 255.0, green: 2.0 / 255.0, blue: 2.0 / 255.0, alpha: 1.0).cgColor self.gl = CAGradientLayer() self.gl.colors = [colorTop, colorBottom] self.gl.locations = [0.0, 1.0] } }
Just modifying the above mentioned answer. func setGradientBackground() { let colorTop = UIColor(red: 255.0/255.0, green: 149.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor let colorBottom = UIColor(red: 255.0/255.0, green: 94.0/255.0, blue: 58.0/255.0, alpha: 1.0).cgColor let gradientLayer = CAGradientLayer() gradientLayer.colors = [colorTop, colorBottom] gradientLayer.locations = [0.0, 1.0] gradientLayer.frame = self.view.bounds self.view.layer.insertSublayer(gradientLayer, at:0) } Then call this method within viewWillAppear override func viewWillAppear(_ animated: Bool) { setGradientBackground() super.viewWillAppear(animated) }
And if you need to change the direction of the gradient you have to use startPoint and endPoint. let gradient: CAGradientLayer = CAGradientLayer() gradient.colors = [UIColor.blue.cgColor, UIColor.red.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: self.view.frame.size.width, height: self.view.frame.size.height) self.view.layer.insertSublayer(gradient, at: 0)
In Swift3 try this: func addGradient(){ let gradient:CAGradientLayer = CAGradientLayer() gradient.frame.size = self.viewThatHoldsGradient.frame.size gradient.colors = [UIColor.white.cgColor,UIColor.white.withAlphaComponent(0).cgColor] //Or any colors self.viewThatHoldsGradient.layer.addSublayer(gradient) }
I have these extensions: #IBDesignable class GradientView: UIView { #IBInspectable var firstColor: UIColor = UIColor.red #IBInspectable var secondColor: UIColor = UIColor.green #IBInspectable var vertical: Bool = true lazy var gradientLayer: CAGradientLayer = { let layer = CAGradientLayer() layer.colors = [firstColor.cgColor, secondColor.cgColor] layer.startPoint = CGPoint.zero return layer }() //MARK: - required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) applyGradient() } override init(frame: CGRect) { super.init(frame: frame) applyGradient() } override func prepareForInterfaceBuilder() { super.prepareForInterfaceBuilder() applyGradient() } override func layoutSubviews() { super.layoutSubviews() updateGradientFrame() } //MARK: - func applyGradient() { updateGradientDirection() layer.sublayers = [gradientLayer] } func updateGradientFrame() { gradientLayer.frame = bounds } func updateGradientDirection() { gradientLayer.endPoint = vertical ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0) } } #IBDesignable class ThreeColorsGradientView: UIView { #IBInspectable var firstColor: UIColor = UIColor.red #IBInspectable var secondColor: UIColor = UIColor.green #IBInspectable var thirdColor: UIColor = UIColor.blue #IBInspectable var vertical: Bool = true { didSet { updateGradientDirection() } } lazy var gradientLayer: CAGradientLayer = { let layer = CAGradientLayer() layer.colors = [firstColor.cgColor, secondColor.cgColor, thirdColor.cgColor] layer.startPoint = CGPoint.zero return layer }() //MARK: - required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) applyGradient() } override init(frame: CGRect) { super.init(frame: frame) applyGradient() } override func prepareForInterfaceBuilder() { super.prepareForInterfaceBuilder() applyGradient() } override func layoutSubviews() { super.layoutSubviews() updateGradientFrame() } //MARK: - func applyGradient() { updateGradientDirection() layer.sublayers = [gradientLayer] } func updateGradientFrame() { gradientLayer.frame = bounds } func updateGradientDirection() { gradientLayer.endPoint = vertical ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0) } } #IBDesignable class RadialGradientView: UIView { #IBInspectable var outsideColor: UIColor = UIColor.red #IBInspectable var insideColor: UIColor = UIColor.green override func awakeFromNib() { super.awakeFromNib() applyGradient() } func applyGradient() { 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) } override func draw(_ rect: CGRect) { super.draw(rect) #if TARGET_INTERFACE_BUILDER applyGradient() #endif } } Usage:
I made an UIView extension to apply a basic gradient to any view extension UIView { func layerGradient() { let layer : CAGradientLayer = CAGradientLayer() layer.frame.size = self.frame.size layer.frame.origin = CGPointMake(0.0,0.0) layer.cornerRadius = CGFloat(frame.width / 20) let color0 = UIColor(red:250.0/255, green:250.0/255, blue:250.0/255, alpha:0.5).CGColor let color1 = UIColor(red:200.0/255, green:200.0/255, blue: 200.0/255, alpha:0.1).CGColor let color2 = UIColor(red:150.0/255, green:150.0/255, blue: 150.0/255, alpha:0.1).CGColor let color3 = UIColor(red:100.0/255, green:100.0/255, blue: 100.0/255, alpha:0.1).CGColor let color4 = UIColor(red:50.0/255, green:50.0/255, blue:50.0/255, alpha:0.1).CGColor let color5 = UIColor(red:0.0/255, green:0.0/255, blue:0.0/255, alpha:0.1).CGColor let color6 = UIColor(red:150.0/255, green:150.0/255, blue:150.0/255, alpha:0.1).CGColor layer.colors = [color0,color1,color2,color3,color4,color5,color6] self.layer.insertSublayer(layer, atIndex: 0) } }
Try This , It's working for me, 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) 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) For more detail description refer Best Answer or you can follow CAGradientLayer From Apple Hopes This is help for some one.
It's easy // MARK: - Gradient extension CAGradientLayer { enum Point { case topLeft case centerLeft case bottomLeft case topCenter case center case bottomCenter case topRight case centerRight case bottomRight var point: CGPoint { switch self { case .topLeft: return CGPoint(x: 0, y: 0) case .centerLeft: return CGPoint(x: 0, y: 0.5) case .bottomLeft: return CGPoint(x: 0, y: 1.0) case .topCenter: return CGPoint(x: 0.5, y: 0) case .center: return CGPoint(x: 0.5, y: 0.5) case .bottomCenter: return CGPoint(x: 0.5, y: 1.0) case .topRight: return CGPoint(x: 1.0, y: 0.0) case .centerRight: return CGPoint(x: 1.0, y: 0.5) case .bottomRight: return CGPoint(x: 1.0, y: 1.0) } } } convenience init(start: Point, end: Point, colors: [CGColor], type: CAGradientLayerType) { self.init() self.startPoint = start.point self.endPoint = end.point self.colors = colors self.locations = (0..<colors.count).map(NSNumber.init) self.type = type } } Use like this:- let fistColor = UIColor.white let lastColor = UIColor.black let gradient = CAGradientLayer(start: .topLeft, end: .topRight, colors: [fistColor.cgColor, lastColor.cgColor], type: .radial) gradient.frame = yourView.bounds yourView.layer.addSublayer(gradient)
Extend UIView with this custom class. GradientView.swift import UIKit class GradientView: UIView { // Default Colors var colors:[UIColor] = [UIColor.redColor(), UIColor.blueColor()] override func drawRect(rect: CGRect) { // Must be set when the rect is drawn setGradient(colors[0], color2: colors[1]) } func setGradient(color1: UIColor, color2: UIColor) { let context = UIGraphicsGetCurrentContext() let gradient = CGGradientCreateWithColors(CGColorSpaceCreateDeviceRGB(), [color1.CGColor, color2.CGColor], [0, 1])! // Draw Path let path = UIBezierPath(rect: CGRectMake(0, 0, frame.width, frame.height)) CGContextSaveGState(context) path.addClip() CGContextDrawLinearGradient(context, gradient, CGPointMake(frame.width / 2, 0), CGPointMake(frame.width / 2, frame.height), CGGradientDrawingOptions()) CGContextRestoreGState(context) } override func layoutSubviews() { // Ensure view has a transparent background color (not required) backgroundColor = UIColor.clearColor() } } Usage gradientView.colors = [UIColor.blackColor().colorWithAlphaComponent(0.8), UIColor.clearColor()] Result
Swift 4 Add a view outlet #IBOutlet weak var gradientView: UIView! Add gradient to the view func setGradient() { let gradient: CAGradientLayer = CAGradientLayer() gradient.colors = [UIColor.red.cgColor, UIColor.blue.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 = gradientView.layer.frame gradientView.layer.insertSublayer(gradient, at: 0) }
This code will work with Swift 3.0 class GradientView: UIView { override open class var layerClass: AnyClass { get{ return CAGradientLayer.classForCoder() } } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) let gradientLayer = self.layer as! CAGradientLayer let color1 = UIColor.white.withAlphaComponent(0.1).cgColor as CGColor let color2 = UIColor.white.withAlphaComponent(0.9).cgColor as CGColor gradientLayer.locations = [0.60, 1.0] gradientLayer.colors = [color2, color1] } }
I mixed the Rohit Sisodia and MGM answers // MARK: - Gradient public enum CAGradientPoint { case topLeft case centerLeft case bottomLeft case topCenter case center case bottomCenter case topRight case centerRight case bottomRight var point: CGPoint { switch self { case .topLeft: return CGPoint(x: 0, y: 0) case .centerLeft: return CGPoint(x: 0, y: 0.5) case .bottomLeft: return CGPoint(x: 0, y: 1.0) case .topCenter: return CGPoint(x: 0.5, y: 0) case .center: return CGPoint(x: 0.5, y: 0.5) case .bottomCenter: return CGPoint(x: 0.5, y: 1.0) case .topRight: return CGPoint(x: 1.0, y: 0.0) case .centerRight: return CGPoint(x: 1.0, y: 0.5) case .bottomRight: return CGPoint(x: 1.0, y: 1.0) } } } extension CAGradientLayer { convenience init(start: CAGradientPoint, end: CAGradientPoint, colors: [CGColor], type: CAGradientLayerType) { self.init() self.frame.origin = CGPoint.zero self.startPoint = start.point self.endPoint = end.point self.colors = colors self.locations = (0..<colors.count).map(NSNumber.init) self.type = type } } extension UIView { func layerGradient(startPoint:CAGradientPoint, endPoint:CAGradientPoint ,colorArray:[CGColor], type:CAGradientLayerType ) { let gradient = CAGradientLayer(start: .topLeft, end: .topRight, colors: colorArray, type: type) gradient.frame.size = self.frame.size self.layer.insertSublayer(gradient, at: 0) } } To Use write:- btnUrdu.layer.cornerRadius = 25 btnUrdu.layer.masksToBounds = true btnUrdu.layerGradient(startPoint: .centerRight, endPoint: .centerLeft, colorArray: [UIColor.appBlue.cgColor, UIColor.appLightBlue.cgColor], type: .axial) Output:
if you want to use HEX instead of RGBA, just drag a new empty .swift and add below mentioned code: import UIKit extension UIColor { convenience init(rgba: String) { var red: CGFloat = 0.0 var green: CGFloat = 0.0 var blue: CGFloat = 0.0 var alpha: CGFloat = 1.0 if rgba.hasPrefix("#") { let index = advance(rgba.startIndex, 1) let hex = rgba.substringFromIndex(index) let scanner = NSScanner(string: hex) var hexValue: CUnsignedLongLong = 0 if scanner.scanHexLongLong(&hexValue) { switch (count(hex)) { case 3: red = CGFloat((hexValue & 0xF00) >> 8) / 15.0 green = CGFloat((hexValue & 0x0F0) >> 4) / 15.0 blue = CGFloat(hexValue & 0x00F) / 15.0 case 4: red = CGFloat((hexValue & 0xF000) >> 12) / 15.0 green = CGFloat((hexValue & 0x0F00) >> 8) / 15.0 blue = CGFloat((hexValue & 0x00F0) >> 4) / 15.0 alpha = CGFloat(hexValue & 0x000F) / 15.0 case 6: red = CGFloat((hexValue & 0xFF0000) >> 16) / 255.0 green = CGFloat((hexValue & 0x00FF00) >> 8) / 255.0 blue = CGFloat(hexValue & 0x0000FF) / 255.0 case 8: red = CGFloat((hexValue & 0xFF000000) >> 24) / 255.0 green = CGFloat((hexValue & 0x00FF0000) >> 16) / 255.0 blue = CGFloat((hexValue & 0x0000FF00) >> 8) / 255.0 alpha = CGFloat(hexValue & 0x000000FF) / 255.0 default: print("Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8") } } else { println("Scan hex error") } } else { print("Invalid RGB string, missing '#' as prefix") } self.init(red:red, green:green, blue:blue, alpha:alpha) } } similarly, drag another empty .swift file and add below mentioned code: class Colors { let colorTop = UIColor(rgba: "##8968CD").CGColor let colorBottom = UIColor(rgba: "#5D478B").CGColor let gl: CAGradientLayer init() { gl = CAGradientLayer() gl.colors = [ colorTop, colorBottom] gl.locations = [ 0.0, 1.0] } } after that in view controller, under class instantiate your 'Color' class like this: let colors = Colors() add a new function: func refresh() { view.backgroundColor = UIColor.clearColor() var backgroundLayer = colors.gl backgroundLayer.frame = view.frame view.layer.insertSublayer(backgroundLayer, atIndex: 0) } state that function in viewDidLoad: refresh() you're done :)) using HEX is way too easy if compared to RGBA. :D
Swift 3 - Uses only textures and SKSpriteNode, doesn't require UIView import Foundation import SpriteKit class GradientSpriteNode : SKSpriteNode { convenience init(size: CGSize, colors: [UIColor], locations: [CGFloat]) { let texture = GradientSpriteNode.texture(size: size, colors: colors, locations: locations) self.init(texture: texture, color:SKColor.clear, size: texture.size()) } private override init(texture: SKTexture!, color: SKColor, size: CGSize) { super.init(texture: texture, color: color, size: size) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private static func texture(size: CGSize, colors: [UIColor], locations: [CGFloat]) -> SKTexture { UIGraphicsBeginImageContext(size) let context = UIGraphicsGetCurrentContext()! let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: colors.map{$0.cgColor} as CFArray, locations: locations)! context.drawLinearGradient(gradient, start: CGPoint(x: size.width / 2, y: 0), end: CGPoint(x: size.width / 2, y: size.height), options: CGGradientDrawingOptions()) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return SKTexture(image: image!) } } Usage: let gradient = GradientSpriteNode( size: CGSize(width: 100, height: 100), colors: [UIColor.red, UIColor.blue], locations: [0.0, 1.0]) addChild(gradient)
I wanted to add a gradient to a view, and then anchor it using auto-layout. class GradientView: UIView { private let gradient: CAGradientLayer = { let layer = CAGradientLayer() let topColor: UIColor = UIColor(red:0.98, green:0.96, blue:0.93, alpha:0.5) let bottomColor: UIColor = UIColor.white layer.colors = [topColor.cgColor, bottomColor.cgColor] layer.locations = [0,1] return layer }() init() { super.init(frame: .zero) gradient.frame = frame layer.insertSublayer(gradient, at: 0) } required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } override func layoutSubviews() { super.layoutSubviews() gradient.frame = bounds } }
Xcode 11 | Swift 5 If anybody is looking for a quick and easy way to add a gradient to a view: extension UIView { func addGradient(colors: [UIColor] = [.blue, .white], locations: [NSNumber] = [0, 2], startPoint: CGPoint = CGPoint(x: 0.0, y: 1.0), endPoint: CGPoint = CGPoint(x: 1.0, y: 1.0), type: CAGradientLayerType = .axial){ let gradient = CAGradientLayer() gradient.frame.size = self.frame.size gradient.frame.origin = CGPoint(x: 0.0, y: 0.0) // Iterates through the colors array and casts the individual elements to cgColor // Alternatively, one could use a CGColor Array in the first place or do this cast in a for-loop gradient.colors = colors.map{ $0.cgColor } gradient.locations = locations gradient.startPoint = startPoint gradient.endPoint = endPoint // Insert the new layer at the bottom-most position // This way we won't cover any other elements self.layer.insertSublayer(gradient, at: 0) } } Examples on how to use the extension: // Testing view.addGradient() // Two Colors view.addGradient(colors: [.init(rgb: 0x75BBDB), .black], locations: [0, 3]) // Full Blown view.addGradient(colors: [.init(rgb: 0x75BBDB), .black], locations: [0, 3], startPoint: CGPoint(x: 0.0, y: 1.5), endPoint: CGPoint(x: 1.0, y: 2.0), type: .axial) Optionally, use the following to input hex numbers .init(rgb: 0x75BBDB) extension UIColor { convenience init(red: Int, green: Int, blue: Int) { self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) } convenience init(rgb: Int) { self.init( red: (rgb >> 16) & 0xFF, green: (rgb >> 8) & 0xFF, blue: rgb & 0xFF ) } }
Use below code : extension UIView { func applyGradient(colours: [UIColor]) -> Void { let gradient: CAGradientLayer = CAGradientLayer() gradient.frame = self.bounds gradient.colors = colours.map { $0.cgColor } gradient.startPoint = CGPoint(x : 0.0, y : 0.5) gradient.endPoint = CGPoint(x :1.0, y: 0.5) self.layer.insertSublayer(gradient, at: 0) } } call this function like : self.mainView.applyGradient(colours: [.green, .blue])
To add gradient into layer, add: let layer = CAGradientLayer() layer.frame = CGRect(x: 64, y: 64, width: 120, height: 120) layer.colors = [UIColor.red.cgColor, UIColor.blue.cgColor] view.layer.addSublayer(layer)
Just Specify the Frame of the View, where you want to show the gradient color. let firstColor = UIColor(red: 69/255, green: 90/255, blue: 195/255, alpha: 1.0).CGColor let secondColor = UIColor(red: 230/255, green: 44/255, blue: 75/255, alpha: 1.0).CGColor let gradientLayer = CAGradientLayer() gradientLayer.colors = [ firstColor, secondColor] gradientLayer.locations = [ 0.0, 1.0] gradientLayer.frame = CGRectMake(0, 0, 375, 64)// You can mention frame here self.view.layer.addSublayer(gradientLayer)
Here's a variation for setting this up in a reusable Util class file In your Xcode project: Create a new Swift class call it UI_Util.swift, and populate it as follows: import Foundation import UIKit class UI_Util { static func setGradientGreenBlue(uiView: UIView) { let colorTop = UIColor(red: 15.0/255.0, green: 118.0/255.0, blue: 128.0/255.0, alpha: 1.0).cgColor let colorBottom = UIColor(red: 84.0/255.0, green: 187.0/255.0, blue: 187.0/255.0, alpha: 1.0).cgColor let gradientLayer = CAGradientLayer() gradientLayer.colors = [ colorTop, colorBottom] gradientLayer.locations = [ 0.0, 1.0] gradientLayer.frame = uiView.bounds uiView.layer.insertSublayer(gradientLayer, at: 0) } } Now you can call the function from any ViewController like so: class AbcViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() UI_Util.setGradientGreen(uiView: self.view) } Thanks to katwal-Dipak's answer for the function code
For swift to apply CAGradientLayer to any object (Horizontal and vertical) func makeGradientColor(`for` object : AnyObject , startPoint : CGPoint , endPoint : CGPoint) -> CAGradientLayer { 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 = startPoint gradient.endPoint = endPoint gradient.frame = CGRect(x: 0.0, y: 0.0, width: object.frame.size.width, height: 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 = self.makeGradientColor(for: vwTop, startPoint: start, endPoint: end) vwTop.layer.insertSublayer(gradient, at: 0) let start1 : CGPoint = CGPoint(x: 1.0, y: 1.0) let end1 : CGPoint = CGPoint(x: 1.0, y: 0.0) let gradient1: CAGradientLayer = self.makeGradientColor(for: vwTop, startPoint: start1, endPoint: end1) vwBottom.layer.insertSublayer(gradient1, at: 0) You can check output here
There is a library called Chameleon (https://github.com/viccalexander/Chameleon) which one can use for gradient colors. It even has styles of gradient to implement. This is how you can add it in swift 4 podfile pod 'ChameleonFramework/Swift', :git => 'https://github.com/ViccAlexander/Chameleon.git', :branch => 'wip/swift4' import ChameleonFramework let colors:[UIColor] = [ UIColor.flatPurpleColorDark(), UIColor.flatWhiteColor() ] view.backgroundColor = GradientColor(.TopToBottom, frame: view.frame, colors: colors)
Here's a swift extension where you can pass any amount of arbitrary colors. It will remove any previous gradients before inserting one and it will return the newly inserted gradient layer for further manipulation if needed: extension UIView { /** Given an Array of CGColor, it will: - Remove all sublayers of type CAGradientLayer. - Create and insert a new CAGradientLayer. - Parameters: - colors: An Array of CGColor with the colors for the gradient fill - Returns: The newly created gradient CAGradientLayer */ func layerGradient(colors c:[CGColor])->CAGradientLayer { self.layer.sublayers = self.layer.sublayers?.filter(){!($0 is CAGradientLayer)} let layer : CAGradientLayer = CAGradientLayer() layer.frame.size = self.frame.size layer.frame.origin = CGPointZero layer.colors = c self.layer.insertSublayer(layer, atIndex: 0) return layer } }
Cleaner code that lets you pass any UIColor to an instance of the GradientLayer class: class GradientLayer { let gradientLayer: CAGradientLayer let colorTop: CGColor let colorBottom: CGColor init(colorTop: UIColor, colorBottom: UIColor) { self.colorTop = colorTop.CGColor self.colorBottom = colorBottom.CGColor gradientLayer = CAGradientLayer() gradientLayer.colors = [colorTop, colorBottom] gradientLayer.locations = [0.0, 1.0] } }
Easy to use extension on swift 3 extension CALayer { func addGradienBorder(colors:[UIColor] = [UIColor.red,UIColor.blue], width:CGFloat = 1) { let gradientLayer = CAGradientLayer() gradientLayer.frame = CGRect(origin: .zero, size: self.bounds.size) gradientLayer.startPoint = CGPoint(x:0.0, y:0.5) gradientLayer.endPoint = CGPoint(x:1.0, y:0.5) gradientLayer.colors = colors.map({$0.cgColor}) let shapeLayer = CAShapeLayer() shapeLayer.lineWidth = width shapeLayer.path = UIBezierPath(rect: self.bounds).cgPath shapeLayer.fillColor = nil shapeLayer.strokeColor = UIColor.black.cgColor gradientLayer.mask = shapeLayer self.addSublayer(gradientLayer) } } use to your view, example yourView.addGradienBorder(color: UIColor.black, opacity: 0.1, offset: CGSize(width:2 , height: 5), radius: 3, viewCornerRadius: 3.0)
If you have view Collection (Multiple View) do this func setGradientBackground() { let v:UIView for v in viewgradian //here viewgradian is your view Collection Outlet name { let layer:CALayer var arr = [AnyObject]() for layer in v.layer.sublayers! { arr.append(layer) } let colorTop = UIColor(red: 216.0/255.0, green: 240.0/255.0, blue: 244.0/255.0, alpha: 1.0).cgColor let colorBottom = UIColor(red: 255.0/255.0, green: 255.0/255.0, blue: 255.0/255.0, alpha: 1.0).cgColor let gradientLayer = CAGradientLayer() gradientLayer.colors = [ colorBottom, colorTop] gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0) gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) gradientLayer.frame = v.bounds v.layer.insertSublayer(gradientLayer, at: 0) } }
For those wanting an Objective C version of the answer. Tested and works on iOS13 // Done here so that constraints have completed and the frame is correct. - (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; UIColor *colorTop = [UIColor colorWithRed:(CGFloat)192.0/255.0 green: 38.0/255.0 blue: 42.0/255.0 alpha:1.0]; UIColor *colorBottom = [UIColor colorWithRed: 35.0/255.0 green: 2.0/255.0 blue: 2.0/255.0 alpha: 1.0]; CAGradientLayer *gl = [CAGradientLayer new]; [gl setColors:#[(id)[colorTop CGColor], (id)[colorBottom CGColor]]]; [gl setLocations:#[#0.0f, #1.0f]]; self.view.backgroundColor = [UIColor clearColor]; CALayer *backgroundLayer = gl; backgroundLayer.frame = self.view.frame; [self.view.layer insertSublayer:backgroundLayer atIndex:0]; }
One thing I noticed is you can't add a gradient to a UILabel without clearing the text. One simple workaround is to use a UIButton and disable user interaction.
SwiftUI: You can use the LinearGradient struct as the first element in a ZStack. As the "bottom" of the ZStack, it will serve as the background color. AngularGradient and RadialGradient are also available. import SwiftUI struct ContentView: View { var body: some View { ZStack { LinearGradient(gradient: Gradient(colors: [.red, .blue]), startPoint: .top, endPoint: .bottom) .edgesIgnoringSafeArea(.all) // Put other content here; it will appear on top of the background gradient } } }
How could I set gradient fixed background on UICollectionView?
I used following code. CAGradientLayer* collectionRadient = [CAGradientLayer layer]; collectionRadient.bounds = self.collectionView.bounds; collectionRadient.anchorPoint = CGPointZero; collectionRadient.colors = [NSArray arrayWithObjects:(id)[startColor CGColor],(id)[endColor CGColor], nil]; [self.collectionView.layer insertSublayer:collectionRadient atIndex:0]; But it drawn on cells included images. so cell was not shown. I want to draw gradient background of UICollectionView under Cells and fixed it when view scrolled. Let me know Please.
Try this... You have to assign a view to use background view. CAGradientLayer* collectionGradient = [CAGradientLayer layer]; collectionGradient.bounds = self.view.bounds; collectionGradient.anchorPoint = CGPointZero; collectionGradient.colors = [NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor],(id)[[UIColor greenColor] CGColor], nil]; UIView *vv = [[UIView alloc] init]; vview.backgroundView = vv; [vview.backgroundView.layer insertSublayer:collectionGradient atIndex:0];
In Swift 3.0 I like to start with a custom class for gradients import UIKit #IBDesignable class GradientView: UIView { //set your start color #IBInspectable var startColor: UIColor = UIColor.black { didSet { updateColors() }} //set your end color #IBInspectable var endColor: UIColor = UIColor.white { didSet { updateColors() }} //you can change anchors and directions #IBInspectable var startLocation: Double = 0.05 { didSet { updateLocations() }} #IBInspectable var endLocation: Double = 0.95 { didSet { updateLocations() }} #IBInspectable var horizontalMode: Bool = false { didSet { updatePoints() }} #IBInspectable var diagonalMode: Bool = false { didSet { updatePoints() }} override class var layerClass: AnyClass { return CAGradientLayer.self } var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer } func updatePoints() { if horizontalMode { gradientLayer.startPoint = diagonalMode ? CGPoint(x: 1, y: 0) : CGPoint(x: 0, y: 0.5) gradientLayer.endPoint = diagonalMode ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0.5) } else { gradientLayer.startPoint = diagonalMode ? CGPoint(x: 0, y: 0) : CGPoint(x: 0.5, y: 0) gradientLayer.endPoint = diagonalMode ? CGPoint(x: 1, y: 1) : CGPoint(x: 0.5, y: 1) } } func updateLocations() { gradientLayer.locations = [startLocation as NSNumber, endLocation as NSNumber] } func updateColors() { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } override func layoutSubviews() { super.layoutSubviews() updatePoints() updateLocations() updateColors() } } With the custom class somewhere in the project and added to the target you just need to do as mentioned earlier and add it as a background view rather than background color or separate normal view sent to the back Implement like so: let gradient = GradientView() gradient.frame = self.view.bounds //add to your collectionView collectionView?.addSubview(gradient) collectionView?.sendSubview(toBack: gradient) self.collectionView?.backgroundView = gradient
Directions Create a view Assign that view to the backgroundView of you collectionView Create a gradient Add gradient layer to collectionView backgroundView Code Swift 4.2 private func addBackgroundGradient() { let collectionViewBackgroundView = UIView() let gradientLayer = CAGradientLayer() gradientLayer.frame.size = view.frame.size // Start and end for left to right gradient gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) gradientLayer.colors = [UIColor.blue.cgColor, UIColor.green.cgColor] collectionView.backgroundView = collectionViewBackgroundView collectionView.backgroundView?.layer.addSublayer(gradientLayer) } If you want a top to bottom gradient all you have to do is remove endPoint and startPoint. You can read more about gradients here. Note that startPoint and endPoint are defined in the coordinate plane from (0.0, 0.0) to (1.0, 1.0)