Remove gradient from subview on rotation - ios

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

Related

Incomprehensible work of the sublayer for cells in the CollectionView. Swift

So, the problem is that the gradient stroke is applied in a way that is not clear to me. At the same time, the problem is only when I do the logic of clicking on the cells as a radio button. If you leave the default (multiple choice), then there is no problem. Maybe somewhere I'm missing changing the height of the view on reloading the collection. Also, if I remove the gradient and include just a stroke, then everything works well. Who has any ideas?
I add the gradient directly in the cell.
class InvestorTypeCollectionViewCell: UICollectionViewCell {
#IBOutlet private weak var cellTitleLabel: UILabel!
#IBOutlet private weak var cellDescriptionLabel: UILabel!
#IBOutlet private weak var checkMarkImageView: UIImageView!
#IBOutlet private weak var itemContainerView: UIView!
weak var delegate: InvestorTypeCollectionViewCellDelegate?
override func prepareForReuse() {
super.prepareForReuse()
checkMarkImageView.image = UIImage(named: "uncheckedBox")
// remove sublayer
itemContainerView.layer.sublayers?.filter{ $0 is CAGradientLayer }.forEach{ $0.removeFromSuperlayer() }
}
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
setNeedsLayout()
layoutIfNeeded()
let size = contentView.systemLayoutSizeFitting(layoutAttributes.size)
var frame = layoutAttributes.frame
frame.size.height = ceil(size.height)
layoutAttributes.frame = frame
return layoutAttributes
}
func configCellWith(item: InvestorTypeModel, indexPath row: Int, selectedIndex: Int? = nil) {
itemContainerView.backgroundColor = UIColor.LIGHT_BLUE_BACKGROUND
itemContainerView.layer.cornerRadius = 8
cellTitleLabel.text = item.itemTitle.uppercased()
cellDescriptionLabel.text = Transformer.share.strippingOutHtmlFrom(text: item.itemDescription)
if item.checkBoxSelected {
diselectUISetUp()
// delegate to change item checkbox to false
delegate?.cellDidSelectedAt(indexPath: row, withCheckbox: false)
} else {
if let selectedIndex = selectedIndex {
if row == selectedIndex {
// select this cell
selectUISetUp()
// delegate to change item checkbox propertie to true
delegate?.cellDidSelectedAt(indexPath: row, withCheckbox: true)
}
}
}
}
private func diselectUISetUp() {
checkMarkImageView.image = UIImage(named: "uncheckedBox")
itemContainerView.layer.borderWidth = 0
itemContainerView.layer.borderColor = UIColor.clear.cgColor
itemContainerView.layer.cornerRadius = 8
}
private func selectUISetUp() {
checkMarkImageView.image = UIImage(named: "checkedBox")
// add gradient
let colors = [UIColor(red: 0.30, green: 0.84, blue: 0.74, alpha: 1.00),
UIColor(red: 0.44, green: 0.35, blue: 0.92, alpha: 1.00),
UIColor(red: 0.26, green: 0.20, blue: 0.87, alpha: 0.93)]
itemContainerView.gradientBorder(width: 1, colors: colors, andRoundCornersWithRadius: 8)
}
}
this is where the gradient is configured
func gradientBorder(width: CGFloat,
colors: [UIColor],
startPoint: CGPoint = CGPoint(x: 0.5, y: 0.0),
endPoint: CGPoint = CGPoint(x: 0.5, y: 1.0),
andRoundCornersWithRadius cornerRadius: CGFloat = 0) {
let existingBorder = gradientBorderLayer()
let border = existingBorder ?? CAGradientLayer()
border.frame = CGRect(x: bounds.origin.x, y: bounds.origin.y,
width: bounds.size.width + width, height: bounds.size.height + width)
border.colors = colors.map { return $0.cgColor }
border.startPoint = startPoint
border.endPoint = endPoint
let mask = CAShapeLayer()
let maskRect = CGRect(x: bounds.origin.x + width/2, y: bounds.origin.y + width/2,
width: bounds.size.width - width, height: bounds.size.height - width)
mask.path = UIBezierPath(roundedRect: maskRect, cornerRadius: cornerRadius).cgPath
mask.fillColor = UIColor.clear.cgColor
mask.strokeColor = UIColor.white.cgColor
mask.lineWidth = width
border.mask = mask
let exists = (existingBorder != nil)
if !exists {
layer.addSublayer(border)
}
}
private func gradientBorderLayer() -> CAGradientLayer? {
let borderLayers = layer.sublayers?.filter { return $0.name == UIView.kLayerNameGradientBorder }
if borderLayers?.count ?? 0 > 1 {
fatalError()
}
return borderLayers?.first as? CAGradientLayer
}
When the data arrives, I change the size of the collection
func refreshData() {
DispatchQueue.main.async {
self.collectionView.reloadData()
guard let dataCount = self.viewModelPresenter?.data?.count else { return }
self.heightConstraintCV.constant = 1000 + 50
}
}
You will find it much easier to use a subclassed UIView that handles its own gradient border.
For example:
#IBDesignable
class GradientBorderView: UIView {
// turns on/off the gradient border
#IBInspectable public var selected: Bool = false { didSet { setNeedsLayout() } }
// default colors
// - can be set at run-time if desired
public var colors: [UIColor] = [UIColor(red: 0.30, green: 0.84, blue: 0.74, alpha: 1.00),
UIColor(red: 0.44, green: 0.35, blue: 0.92, alpha: 1.00),
UIColor(red: 0.26, green: 0.20, blue: 0.87, alpha: 0.93)]
{
didSet {
setNeedsLayout()
}
}
// default boder line width, corner radius, and inset-from-edges
// - can be set at run-time if desired
#IBInspectable public var bWidth: CGFloat = 1 { didSet { setNeedsLayout() } }
#IBInspectable public var cRadius: CGFloat = 8 { didSet { setNeedsLayout() } }
#IBInspectable public var inset: CGFloat = 0.5 { didSet { setNeedsLayout() } }
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
private var gLayer: CAGradientLayer {
return self.layer as! CAGradientLayer
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() {
gLayer.startPoint = CGPoint(x: 0.5, y: 0.0)
gLayer.endPoint = CGPoint(x: 0.5, y: 1.0)
}
override func layoutSubviews() {
super.layoutSubviews()
let shapeLayer = CAShapeLayer()
if selected {
gLayer.colors = colors.compactMap( {$0.cgColor })
// make shapeLayer path the size of view inset by "inset"
// with rounded corners
// strokes are centered, so inset must be at least 1/2 of the border width
let mInset = max(inset, bWidth * 0.5)
shapeLayer.path = UIBezierPath(roundedRect: bounds.insetBy(dx: mInset, dy: mInset), cornerRadius: cRadius).cgPath
// clear fill color
shapeLayer.fillColor = UIColor.clear.cgColor
// stroke color can be any non-clear color
shapeLayer.strokeColor = UIColor.black.cgColor
shapeLayer.lineWidth = bWidth
} else {
// we'll mask with an empty path
shapeLayer.path = UIBezierPath().cgPath
}
gLayer.mask = shapeLayer
}
}
So, use a GradientBorderView instead of a UIView:
#IBOutlet private weak var itemContainerView: GradientBorderView!
and all you have to do is set its selected property to show/hide the border:
itemContainerView.selected = true
or you could set .isHidden to show/hide it.
The "gradient border" will automatically update any time the view size changes.
The view is also marked #IBDesignable with #IBInspectable properties, so you can see how it looks while designing in Storyboard (note: selected is false by default, so you won't see anything unless you change that to true).

How to apply gradient border color to UIButton?

I want to achieve something like this. I have searched for this. I get suggestions that I can put a gradient view behind the button with height and width more than the button. But I want exact corner radius and border color with gradients.
You can make your own BorderedButton subclass that will:
Add gradient sublayer;
Create shape layer consisting of the path of the border; And
Use that shape layer as mask to the gradient layer to yield gradient border in the shape of the path.
E.g.:
#IBDesignable
class BorderedButton: UIButton {
#IBInspectable var lineWidth: CGFloat = 3 { didSet { setNeedsLayout() } }
#IBInspectable var cornerRadius: CGFloat = 10 { didSet { setNeedsLayout() } }
let borderLayer: CAGradientLayer = {
let borderLayer = CAGradientLayer()
borderLayer.type = .axial
borderLayer.colors = [#colorLiteral(red: 0.6135130525, green: 0.3031745553, blue: 0.9506058097, alpha: 1).cgColor, #colorLiteral(red: 0.9306473136, green: 0.1160953864, blue: 0.8244602084, alpha: 1).cgColor]
borderLayer.startPoint = CGPoint(x: 0, y: 1)
borderLayer.endPoint = CGPoint(x: 1, y: 0)
return borderLayer
}()
override init(frame: CGRect = .zero) {
super.init(frame: frame)
configure()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
configure()
}
override func layoutSubviews() {
super.layoutSubviews()
borderLayer.frame = bounds
let mask = CAShapeLayer()
let rect = bounds.insetBy(dx: lineWidth / 2, dy: lineWidth / 2)
mask.path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).cgPath
mask.lineWidth = lineWidth
mask.fillColor = UIColor.clear.cgColor
mask.strokeColor = UIColor.white.cgColor
borderLayer.mask = mask
}
}
private extension BorderedButton {
func configure() {
layer.addSublayer(borderLayer)
}
}
Note that:
I update the frame of the gradient layer and the path that is used as the mask inside the layoutSubviews method, which ensures that the border is correctly rendered if the size changes (e.g. you’re using constraints to define the size of the view).
I’ve made this #IBDesignable so that you can even add this to a storyboard and you’ll see it rendered correctly.
I’ve made the cornerRadius and lineWidth to be #IBInspectable so that you can adjust these in IB (and because they have didSet observers that sets “needs layout”, they’ll ensure that changes are observable in the storyboard).
Anyway, that yields:
extension CALayer {
func addGradienBorder(colors:[UIColor],width:CGFloat = 1) {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(origin: CGPointZero, size: self.bounds.size)
gradientLayer.startPoint = CGPointMake(0.0, 0.5)
gradientLayer.endPoint = CGPointMake(1.0, 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.blackColor().CGColor
gradientLayer.mask = shapeLayer
self.addSublayer(gradientLayer)
}

Adding shadow layer with rounded corners on custom UIButton subclass

I'm trying to apply a shadow and rounded corners on custom UIButton subclass to use in on the storyboard
Here is my code :
import UIKit
class customButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
setGradientBackground()
setShadowLayer()
}
private func setup() {
clipsToBounds = true
layer.cornerRadius = 8
}
private func setGradientBackground() {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.colors = [
UIColor(red: 0.18, green: 0.5, blue: 0.93, alpha: 1).cgColor,
UIColor(red: 0.18, green: 0.61, blue: 0.86, alpha: 1).cgColor
]
gradientLayer.locations = [0,1]
gradientLayer.startPoint = CGPoint(x: 0.25, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
layer.insertSublayer(gradientLayer, at: 0)
}
private func setShadowLayer(){
let subLayer = CALayer()
subLayer.frame = bounds
subLayer.shadowPath = UIBezierPath(roundedRect: bounds, cornerRadius: 8).cgPath
subLayer.shadowColor = UIColor.red.cgColor
subLayer.shadowOpacity = 1
subLayer.shadowRadius = 12
subLayer.shadowOffset = CGSize(width: 0, height: 2)
layer.insertSublayer(subLayer, at: 0)
}
}
When I'm setting the clipsToBounds to true it remove the shadow, so I decided to add a new sublayer func setShadowLayer() and nothing worked so far.
Try also setting the .cornerRadius on the gradient layer:
gradientLayer.endPoint = CGPoint(x: 0.75, y: 0.5)
// add this line
gradientLayer.cornerRadius = 8
layer.insertSublayer(gradientLayer, at: 0)
My result:

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

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

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