Xcode 6 gradient layer to resize correctly with object resizing a universal app - ios

Is there a preferred method to add an auto-resizing gradient layer to a UIView or UILabel that will conform to the autolayout constraints set in IB (Xcode 6) and a wAny hAny canvass, using Swift?
For example, I can start with two labels that span universal widths using constraints using an wAny and hAny canvass, but when running the app on an iPad2 simulator, the labels size correctly but not the gradient layer.
I've looked into willAnimateRotationToInterfaceOrientation and adding a notification based on UIDeviceOrientationDidChangeNotification, but this is not helpful when the app is loaded in landscape (although once rotated the layers are redrawn and do match).
The only solution I have found is to manally adjust the width of each button to conform to the maximum view width of each (iPad2 in landscape) in the Utilities side panel in Xcode, but that creates "misplaced view" warnings and a messy canvass.
Any other ideas?
Here is what I have:
In viewDidLoad, I send each button through this:
Example:
//viewDidLoad --
self.configButton(self.button1!)
self.configButton(self.button2!)
self.configButton(self.button3!)
//
func configButton(theButton: UIButton){
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame.size = theButton.frame.size
theButton.layer.insertSublayer(gradient, atIndex: 0)
let topR = CGFloat(13.0)
let topG = CGFloat(55.0)
let topB = CGFloat(112.0)
let bottomR = CGFloat(90.0)
let bottomG = CGFloat(126.0)
let bottomB = CGFloat(167.0)
let colorTop = UIColor(red: CGFloat(topR/255.0), green: CGFloat(topG/255.0), blue: CGFloat(topB/255.0), alpha: 1.0).CGColor
let colorBottom = UIColor(red: CGFloat(bottomR/255.0), green: CGFloat(bottomR/255.0), blue: CGFloat(bottomR/255.0), alpha: 1.0).CGColor
let gradientColors: [AnyObject] = [colorTop, colorBottom]
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let theCornerRadius: CGFloat = 5.0
theButton.layer.cornerRadius = theCornerRadius
theButton.layer.masksToBounds = true
theButton.layer.borderWidth = 0.1
theButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}

I would do this by subclassing UIButton, and putting the configureButton method, minus the layer sizing line, in that class. You can then set the frame of the gradient layer equal to the bounds of its super layer (the button's default layer) in layoutSublayersOfLayer because that layer automatically resizes to keep it the same size as the button. You don't need any code in the controller with this approach.
class RDButton: UIButton {
let gradient = CAGradientLayer()
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureButton()
}
func configureButton() {
self.layer.insertSublayer(gradient, atIndex: 0)
let topR = CGFloat(13.0)
let topG = CGFloat(55.0)
let topB = CGFloat(112.0)
let bottomR = CGFloat(90.0)
let bottomG = CGFloat(126.0)
let bottomB = CGFloat(167.0)
let colorTop = UIColor(red: CGFloat(topR/255.0), green: CGFloat(topG/255.0), blue: CGFloat(topB/255.0), alpha: 1.0).CGColor
let colorBottom = UIColor(red: CGFloat(bottomR/255.0), green: CGFloat(bottomR/255.0), blue: CGFloat(bottomR/255.0), alpha: 1.0).CGColor
let gradientColors: [AnyObject] = [colorTop, colorBottom]
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let theCornerRadius: CGFloat = 5.0
self.layer.cornerRadius = theCornerRadius
self.layer.masksToBounds = true
self.layer.borderWidth = 0.1
self.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
gradient.frame = layer.bounds
}
}

Here is what I came up with to pass gradient colors to this new button class:
import Foundation
import UIKit
import QuartzCore
class RDButton: UIButton{
let gradient = CAGradientLayer()
var gradientColors: [Int] = []
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func configureButton(thecolors: [Int]) {
println("thecolors: \(thecolors)")
self.layer.insertSublayer(gradient, atIndex: 0)
let topR = CGFloat(thecolors[0])
let topG = CGFloat(thecolors[1])
let topB = CGFloat(thecolors[2])
let bottomR = CGFloat(thecolors[3])
let bottomG = CGFloat(thecolors[4])
let bottomB = CGFloat(thecolors[5])
let colorTop = UIColor(red: CGFloat(topR/255.0), green: CGFloat(topG/255.0), blue: CGFloat(topB/255.0), alpha: 1.0).CGColor
let colorBottom = UIColor(red: CGFloat(bottomR/255.0), green: CGFloat(bottomR/255.0), blue: CGFloat(bottomR/255.0), alpha: 1.0).CGColor
let gradientColors: [AnyObject] = [colorTop, colorBottom]
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
let theCornerRadius: CGFloat = 5.0
self.layer.cornerRadius = theCornerRadius
self.layer.masksToBounds = true
self.layer.borderWidth = 0.1
self.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
}
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
gradient.frame = layer.bounds
}
}
In the View Controller, I did this:
#IBOutlet weak var mySweetButton: RDButton?
// etc for each button
let buttonsgradient = [13,55,112,90,126,167]
override func viewDidLoad(){
super.viewDidLoad()
//...
mySweetButton?.configureButton(buttonsgradient)
/// etc for each button
// ...
}
Probably not the optimal way of doing this ...

Related

Remove gradient from subview on rotation

I can remove the gradient from the subview fine, But If a button is added to the subview I can not remove the gradient layer. How can I Remove gradient from subview on rotation.
Here is the code for the View Controller.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var buttonOutlet: UIButton!
#IBOutlet weak var labelOutlet: UILabel!
#IBOutlet weak var mySubView: UIView!
#IBOutlet weak var mySubView2: UIView!
#IBOutlet weak var mySubView3: UIView!
override func viewDidLayoutSubviews() {
addGradient()
}
fileprivate func addGradient() {
mySubView.mainGradientBackground()
mySubView2.subGradientBackground()
mySubView3.subGradientBackground()
labelOutlet.labelTextfieldShadow()
buttonOutlet.buttonGradientBackground(cornerRadius: 10, shadowRadius: 3)
}
/// This will let you know when the device rotates.
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isValidInterfaceOrientation {
removeSublayer(mySubView, layerIndex: 0)
removeSublayer(mySubView, layerIndex: 0)
removeSublayer(mySubView2, layerIndex: 0)
removeSublayer(mySubView2, layerIndex: 0)
removeSublayer(mySubView3, layerIndex: 0)
removeSublayer(mySubView3, layerIndex: 0)
removeSublayer(buttonOutlet, layerIndex: 0)
removeSublayer(buttonOutlet, layerIndex: 0)
removeSublayer(labelOutlet, layerIndex: 0)
}
}
func removeSublayer(_ view: UIView, layerIndex index: Int) {
guard let sublayers = view.layer.sublayers else {
print("The view does not have any sublayers.")
return
}
if sublayers.count > index {
view.layer.sublayers!.remove(at: index)
} else {
print("There are not enough sublayers to remove that index.")
}
}
}
This is the Gradient Extension used to color any views I need.
import Foundation
import UIKit
extension UIView {
/// Use this to set a gradient for the background.
/// = This is a gradient with no shadow.
func mainGradientBackground() {
let color = Color()
let leading = color.backgroundLeading
let trailing = color.backgroundTrailing
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [leading, trailing]
gradientLayer.locations = [0,0.3]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
layer.insertSublayer(gradientLayer, at: 0)
}
/// Use this to set a gradient for the background.
/// = This is a gradient with a shadow.
func subGradientBackground() {
let corner: CGFloat = 15
let color = Color()
let leading = color.subBackgroundLeading
let trailing = color.subBackgroundTrailing
let borderColor = color.subBorderColor
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [leading, trailing]
gradientLayer.locations = [0,0.3,1]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.borderColor = borderColor
gradientLayer.borderWidth = 4
gradientLayer.cornerRadius = corner
layer.insertSublayer(gradientLayer, at: 0)
let subLayer = CALayer()
subLayer.frame = bounds
subLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: corner - 2).cgPath
subLayer.shadowColor = UIColor.red.cgColor
subLayer.shadowOpacity = 1
subLayer.shadowRadius = 2
subLayer.shadowOffset = CGSize(width: 3, height: 4)
layer.insertSublayer(subLayer, at: 0)
}
/// Use this to set a gradient for the background.
/// = This is a gradient with a shadow.
func subGradientBackgroundShadow() {
let corner: CGFloat = 15
let subLayer = CALayer()
subLayer.frame = bounds
subLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: corner - 2).cgPath
subLayer.shadowColor = UIColor.red.cgColor
subLayer.shadowOpacity = 1
subLayer.shadowRadius = 2
subLayer.shadowOffset = CGSize(width: 3, height: 4)
layer.insertSublayer(subLayer, at: 0)
}
/// LabelShadow
/// - Parameter masksToBounds: Keep the layer in its bound
/// - Exp.. Set to true to keep text in textview in its frame.
/// - Exp.. Set to false to let shadow out side the frame.
/// - Defalr corner radius is 7
func labelTextfieldShadow() {
let color = Color()
let dark = color.labelBackgroundDark
let borderColor = color.labelBorderColor
layer.masksToBounds = false
layer.borderWidth = 0.5
layer.borderColor = borderColor
layer.cornerRadius = 7
self.backgroundColor = dark
layer.shadowRadius = 7
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 3, height: 3)
layer.shadowOpacity = 0.6
}
/// Button gradient
/// - Parameter cornerRadius: Set the radius of the button. Use outlet height / 2
/// - Parameter shadowRadius: Set the shadow radius. 2
func buttonGradientBackground(cornerRadius: CGFloat, shadowRadius: CGFloat) {
let color = Color()
let leading = color.buttonLeading
let middle = color.buttonMiddle
let trailing = color.buttonTrailing
let borderColor = color.buttonBorderColor
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [leading, middle, trailing]
gradientLayer.locations = [0,0.3,1]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.borderColor = borderColor
gradientLayer.borderWidth = 1
gradientLayer.cornerRadius = cornerRadius
layer.insertSublayer(gradientLayer, at: 0)
let subLayer = CALayer()
subLayer.frame = bounds
subLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius - 2).cgPath
subLayer.shadowColor = UIColor.black.cgColor
subLayer.shadowOpacity = 1
subLayer.shadowRadius = shadowRadius
subLayer.shadowOffset = CGSize(width: 2, height: 3)
layer.insertSublayer(subLayer, at: 0)
}
}
Here is four screen shots of the screen.
This is a color struct to color all of the views.. Main view, Sub view, Labels and Buttons.
struct Color {
// Use these colors to style the main background.
/// This color is used for the main view's background leading greadient color.
let backgroundLeading = #colorLiteral(red: 0.2745098039, green: 0.231372549, blue: 0.231372549, alpha: 1).cgColor
/// TThis color is used for the main view's background trailing greadient color.
let backgroundTrailing = #colorLiteral(red: 0.2784313725, green: 0.2352941176, blue: 0.2352941176, alpha: 1).cgColor
// Use these colors to style the sub view.
/// This color is used for the sub view's leading greadient color.
let subBackgroundLeading = #colorLiteral(red: 0.1960784314, green: 0.2549019608, blue: 0.2549019608, alpha: 1).cgColor
/// This color is used for the sub view's trailing greadient color.
let subBackgroundTrailing = #colorLiteral(red: 0.1012082246, green: 0.2111029723, blue: 0.1947238818, alpha: 1).cgColor
/// This color is used for the sub view's border.
let subBorderColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1).cgColor
// Use these colors to style the labels.
/// This color is used for editing text background.
let labelBackgroundLight = #colorLiteral(red: 0.921431005, green: 0.9214526415, blue: 0.9214410186, alpha: 1) as UIColor
/// This color is used for disabled text background.
let labelBackgroundDark = #colorLiteral(red: 0.1843137255, green: 0.137254902, blue: 0.1384684741, alpha: 1) as UIColor
/// This color is used for the label's border.
let labelBorderColor = #colorLiteral(red: 0.6, green: 0.6, blue: 0.6, alpha: 1).cgColor
// Use these colors to style the buttons.
/// This color is used for the button's leading greadient color.
let buttonLeading = #colorLiteral(red: 0.1960784314, green: 0.2549019608, blue: 0.2549019608, alpha: 1).cgColor
/// This color is used for the button's middle greadient color.
let buttonMiddle = #colorLiteral(red: 0.2, green: 0.2, blue: 0.2, alpha: 1).cgColor
/// This color is used for the button's trailing greadient color.
let buttonTrailing = #colorLiteral(red: 0.1012082246, green: 0.2111029723, blue: 0.1947238818, alpha: 1).cgColor
/// This color is used for the button's border color.
let buttonBorderColor = #colorLiteral(red: 0.6000000238, green: 0.6000000238, blue: 0.6000000238, alpha: 1).cgColor
// Use these colors for the text
/// This color is used for the deiabled label and text field.
let labelDisableColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
/// This color is used for the enabled label and text field.
let labelEnableColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
/// The text color white.
let labelWhiteTextColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
/// The text color Black
let labelBlackTextColor = #colorLiteral(red: 0, green: 0, blue: 0, alpha: 1)
}
Instead of assuming that a layer is at a given index, just retain the laters you want to remove as an instance variable (or in an array of CALayers) and then tell the layer(s) to remove themselves from their super layer.
For example:
var layersToRemoveLater = [CALayer]()
...
layersToRemoveLater.append(someLayer)
layersToRemoveLater.append(someOtherLayer)
...
layersToRemoveLater.forEach { $0.removeFromSuperLayer() }
The way to do this was to change each one of the extension to a class. With extension you have to take care of all of the layout your self. By using these as a class you can set each Button, Sub view Ect... in the storyboard, this way the view itself will redrawing the layout.
/// Use this to set a greadient for the background.
/// = This is a greadient with no shadow.
class MainGradientView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
guard let gradientLayer = self.layer as? CAGradientLayer else {
return;
}
let color = Color()
let leading = color.backgroundLeading
let trailing = color.backgroundTrailing
gradientLayer.frame = bounds
gradientLayer.colors = [leading, trailing]
gradientLayer.locations = [0,0.3]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}
/// Use this to set a greadient for the background.
/// = This is a greadient with a shadow.
class SubGradientView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
guard let gradientLayer = self.layer as? CAGradientLayer else {
return;
}
let corner: CGFloat = 15
let color = Color()
let leading = color.subBackgroundLeading
let trailing = color.subBackgroundTrailing
let borderColor = color.subBorderColor
gradientLayer.frame = self.bounds
gradientLayer.colors = [leading, trailing]
gradientLayer.locations = [0,0.3,1]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.borderColor = borderColor
gradientLayer.borderWidth = 0.4
gradientLayer.cornerRadius = corner
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 1
self.layer.shadowRadius = 2
self.layer.shadowOffset = CGSize(width: 3, height: 4)
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}
/// Use this to set a greadient for the background.
/// = This is a greadient with no shadow.
class TabBarGradientView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
guard let gradientLayer = self.layer as? CAGradientLayer else {
return;
}
let color = Color()
let leading = color.buttonLeading
let middle = color.buttonMiddle
let trailing = color.buttonTrailing
let borderColor = color.buttonBorderColor
gradientLayer.frame = bounds
gradientLayer.colors = [leading, middle, trailing]
gradientLayer.locations = [0,0.3,1]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.borderColor = borderColor
gradientLayer.borderWidth = 1
gradientLayer.cornerRadius = self.frame.width / 50
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}
/// Use this to set a greadient for the background.
/// = This is a greadient with a shadow.
class ButtonGradientView: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
guard let gradientLayer = self.layer as? CAGradientLayer else {
return;
}
let color = Color()
let leading = color.buttonLeading
let middle = color.buttonMiddle
let trailing = color.buttonTrailing
let borderColor = color.buttonBorderColor
gradientLayer.frame = bounds
gradientLayer.colors = [leading, middle, trailing]
gradientLayer.locations = [0,0.3,1]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.borderColor = borderColor
gradientLayer.borderWidth = 1
gradientLayer.cornerRadius = self.frame.height / 2
self.layer.frame = bounds
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 1
self.layer.shadowRadius = 2
self.layer.shadowOffset = CGSize(width: 2, height: 3)
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}
/// Use this to set a greadient for the keypad switch.
/// = This is a greadient with a shadow.
class SwitchGradientView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
autoresizingMask = [.flexibleWidth, .flexibleHeight]
guard let gradientLayer = self.layer as? CAGradientLayer else {
return;
}
let color = Color()
let leading = color.buttonLeading
let middle = color.buttonMiddle
let trailing = color.buttonTrailing
let borderColor = color.buttonBorderColor
gradientLayer.frame = bounds
gradientLayer.colors = [leading, middle, trailing]
gradientLayer.locations = [0,0.3,1]
gradientLayer.startPoint = CGPoint(x: 0.05, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
gradientLayer.borderColor = borderColor
gradientLayer.borderWidth = 1
gradientLayer.cornerRadius = 9
self.layer.frame = bounds
self.layer.shadowColor = UIColor.black.cgColor
self.layer.shadowOpacity = 1
self.layer.shadowRadius = 2
self.layer.shadowOffset = CGSize(width: 2, height: 3)
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
}

Gradient background iPhone dimensions on iPad

I have a problem setting gradient background on iPad. On iPhones everything is ok but when I use iPad the gradient background has iPhone dimensions.
The code that I use to make the gradient is the on below.
func setGradientToTableView(tableView: UITableView) {
let gradientBackgroundColors = [UIColor(red: 190.0/255.0, green: 219.0/255.0, blue: 0.0/255.0, alpha: 1).cgColor, UIColor(red: 13.0/255.0, green: 227.0/255.0, blue: 97.0/255.0, alpha: 1).cgColor]
let gradientLayer = CAGradientLayer()
gradientLayer.colors = gradientBackgroundColors
gradientLayer.startPoint = CGPoint(x: 0,y: 0)
gradientLayer.frame = tableView.bounds
let backgroundView = UIView(frame: tableView.bounds)
backgroundView.layer.insertSublayer(gradientLayer, at: 0)
tableView.backgroundView = backgroundView
}
You need to set frame for layer in viewDidLayoutSubviews:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
tableView.backgroundView?.layer.sublayers?.forEach { $0.frame = tableView.bounds }
}
Also, as #Jan Schlorf suggests in comment, you can store your layer as property:
lazy var gradientLayer = CAGradientLayer()
//...
func setGradientToTableView(tableView: UITableView) {
let gradientBackgroundColors = [UIColor(red: 190.0/255.0, green: 219.0/255.0, blue: 0.0/255.0, alpha: 1).cgColor, UIColor(red: 13.0/255.0, green: 227.0/255.0, blue: 97.0/255.0, alpha: 1).cgColor]
gradientLayer.colors = gradientBackgroundColors
gradientLayer.startPoint = CGPoint(x: 0,y: 0)
gradientLayer.frame = tableView.bounds
let backgroundView = UIView(frame: tableView.bounds)
backgroundView.layer.insertSublayer(gradientLayer, at: 0)
tableView.backgroundView = backgroundView
}
//...
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
gradientLayer.frame = tableView.bounds
}
I assume you didn't use autolayout?
Check your tableviewsize when starting your app with iPad Simulator. I think it is not as big as you think it is.

How to set gradient colour to UIButton in background?

Am trying to set CAGradientLayer for UIButton.
But it is not getting exactly. am I doing any wrong ?
am getting output like
You can see there is some horizontal line
Here is the code which I tried
let topColor = UIColor(red: 0.62, green: 0.38, blue: 1, alpha: 1).cgColor
let bottomColor = UIColor(red: 0.87, green: 0.51, blue: 0.93, alpha: 1).cgColor
let gradientLayer = CAGradientLayer()
gradientLayer.frame = myButton.bounds
gradientLayer.colors = [topColor, bottomColor]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
myButton.layer.insertSublayer(myButton, at: 0)
Create a Designable custom class like this
import Foundation
import UIKit
#IBDesignable class CustomButton:UIButton {
#IBInspectable var firstColor:UIColor = UIColor.clear {
didSet {
updateUI()
}
}
#IBInspectable var secondColor:UIColor = UIColor.clear {
didSet{
updateUI()
}
}
override class var layerClass: AnyClass {
get {
return CAGradientLayer.self
}
}
func updateUI(){
let gradient: CAGradientLayer = self.layer as! CAGradientLayer
gradient.frame = self.bounds
gradient.colors = [firstColor,secondColor].map { $0.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)
}
}
from your Identity inspector set your UIbutton class to CustomButtom like this
from your Attribute inspector change your gradient color to whatever you want to set
and finally your button will be like this
if you want to change the starting and endpoint to those colors
just play around with these two values
gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
You have to update the gradient's frame when the view is laid out:
override func viewDidLayoutSubviews() {
myButton.layer.sublayers?.first?.frame = myButton.bounds
}

Gradient Layer in Navigation Bar in iOS 11

After adding gradient layer in navigation bar, I don't see any right/left bar button items when i run on iOS 11. But the same code displays well on iOS 10/9..
Can anyone provide your valuable suggestions to fix this
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
setUpGradientNavigationBar()
}
func setUpGradientNavigationBar() {
let lightRedColor = UIColor(red: CGFloat(197/255.0), green: 47/255.0, blue: 40/255.0, alpha: 1.0).cgColor
let mediumRedColor = UIColor(red: CGFloat(176/255.0), green: 42/255.0, blue: 36/255.0, alpha: 1.0).cgColor
let darkRedColor = UIColor(red: CGFloat(106/255.0), green: 25/255.0, blue: 22/255.0, alpha: 1.0).cgColor
let colors = NSArray(objects: lightRedColor, mediumRedColor, darkRedColor)
let gradientLayer = getGradientLayerForColors(colors, location: 0.5, andFrame: self.navigationController?.navigationBar.bounds)
self.navigationController?.navigationBar.barTintColor = UIColor.black
self.navigationController?.navigationBar.layer.insertSublayer(gradientLayer, at: 1)
self.navigationController?.navigationBar.tintColor = UIColor.white
self.navigationController?.navigationBar.topItem?.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.cancel, target: nil, action: nil)
}
func getGradientLayerForColors(_ colors: NSArray, location:CGFloat, andFrame frame:CGRect?) -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.colors = colors as [AnyObject]
gradientLayer.locations = [0.0,NSNumber.init(value: Float(location))]
gradientLayer.frame = frame!
return gradientLayer
}
Swift 5
Includes status bar in gradient.
Doesn't use an image.
Uses transparency so content is visible through nav bar.
extension UINavigationBar {
func addGradient(_ toAlpha: CGFloat, _ color: UIColor) {
let gradient = CAGradientLayer()
gradient.colors = [
color.withAlphaComponent(toAlpha).cgColor,
color.withAlphaComponent(toAlpha).cgColor,
color.withAlphaComponent(0).cgColor
]
gradient.locations = [0, 0.8, 1]
var frame = bounds
frame.size.height += UIApplication.shared.statusBarFrame.size.height
frame.origin.y -= UIApplication.shared.statusBarFrame.size.height
gradient.frame = frame
layer.insertSublayer(gradient, at: 1)
}
}

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

Resources