Programmatically create a UIView with color gradient - ios
I'm trying to generate a view with a gradient color background (A solid color to transparent) at runtime. Is there a way of doing that?
Objective-C:
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.colors = #[(id)[UIColor whiteColor].CGColor, (id)[UIColor blackColor].CGColor];
[view.layer insertSublayer:gradient atIndex:0];
Swift:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 320, height: 50))
let gradient = CAGradientLayer()
gradient.frame = view.bounds
gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
view.layer.insertSublayer(gradient, at: 0)
Info: use startPoint and endPoint to change direction of gradient.
If there are any other views added onto this UIView (such as a UILabel), you may want to consider setting the background color of those UIView’s to [UIColor clearColor] so the gradient view is presented instead of the background color for sub views. Using clearColor has a slight performance hit.
You can create a custom class GradientView:
Swift 5
class GradientView: UIView {
override open class var layerClass: AnyClass {
return CAGradientLayer.classForCoder()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let gradientLayer = layer as! CAGradientLayer
gradientLayer.colors = [UIColor.white.cgColor, UIColor.black.cgColor]
}
}
In the storyboard, set the class type to any view that you want to have gradient background:
This is better in the following ways:
No need to set frame of CLayer
Use NSConstraint as usual on the UIView
Don't need to create sublayers (less memory use)
Try This it worked like a charm for me,
Objective C
I have set RGB gradient background Color to UIview
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,35)];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.startPoint = CGPointZero;
gradient.endPoint = CGPointMake(1, 1);
gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithRed:34.0/255.0 green:211/255.0 blue:198/255.0 alpha:1.0] CGColor],(id)[[UIColor colorWithRed:145/255.0 green:72.0/255.0 blue:203/255.0 alpha:1.0] CGColor], nil];
[view.layer addSublayer:gradient];
UPDATED :-
Swift3 +
Code :-
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 details description refer CAGradientLayer Doc
Hope this is help for some one .
This is my recommended approach.
To promote reusability, I'd say create a category of CAGradientLayer and add your desired gradients as class methods. Specify them in the header file like this :
#import <QuartzCore/QuartzCore.h>
#interface CAGradientLayer (SJSGradients)
+ (CAGradientLayer *)redGradientLayer;
+ (CAGradientLayer *)blueGradientLayer;
+ (CAGradientLayer *)turquoiseGradientLayer;
+ (CAGradientLayer *)flavescentGradientLayer;
+ (CAGradientLayer *)whiteGradientLayer;
+ (CAGradientLayer *)chocolateGradientLayer;
+ (CAGradientLayer *)tangerineGradientLayer;
+ (CAGradientLayer *)pastelBlueGradientLayer;
+ (CAGradientLayer *)yellowGradientLayer;
+ (CAGradientLayer *)purpleGradientLayer;
+ (CAGradientLayer *)greenGradientLayer;
#end
Then in your implementation file, specify each gradient with this syntax :
+ (CAGradientLayer *)flavescentGradientLayer
{
UIColor *topColor = [UIColor colorWithRed:1 green:0.92 blue:0.56 alpha:1];
UIColor *bottomColor = [UIColor colorWithRed:0.18 green:0.18 blue:0.18 alpha:1];
NSArray *gradientColors = [NSArray arrayWithObjects:(id)topColor.CGColor, (id)bottomColor.CGColor, nil];
NSArray *gradientLocations = [NSArray arrayWithObjects:[NSNumber numberWithInt:0.0],[NSNumber numberWithInt:1.0], nil];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.colors = gradientColors;
gradientLayer.locations = gradientLocations;
return gradientLayer;
}
Then simply import this category in your ViewController or any other required subclass, and use it like this :
CAGradientLayer *backgroundLayer = [CAGradientLayer purpleGradientLayer];
backgroundLayer.frame = self.view.frame;
[self.view.layer insertSublayer:backgroundLayer atIndex:0];
extension UIView {
func applyGradient(isVertical: Bool, colorArray: [UIColor]) {
layer.sublayers?.filter({ $0 is CAGradientLayer }).forEach({ $0.removeFromSuperlayer() })
let gradientLayer = CAGradientLayer()
gradientLayer.colors = colorArray.map({ $0.cgColor })
if isVertical {
//top to bottom
gradientLayer.locations = [0.0, 1.0]
} else {
//left to right
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
}
backgroundColor = .clear
gradientLayer.frame = bounds
layer.insertSublayer(gradientLayer, at: 0)
}
}
USAGE
someView.applyGradient(isVertical: true, colorArray: [.green, .blue])
Since I only needed one type of gradient throughout my app I created a subclass of UIView and preconfigured the gradient layer on initialization with fixed colors.
The initializers of UIView call the configureGradientLayer-method, which configures the CAGradientLayer:
DDGradientView.h:
#import <UIKit/UIKit.h>
#interface DDGradientView : UIView {
}
#end
DDGradientView.m:
#import "DDGradientView.h"
#implementation DDGradientView
// Change the views layer class to CAGradientLayer class
+ (Class)layerClass
{
return [CAGradientLayer class];
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self) {
[self configureGradientLayer];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self) {
[self configureGradientLayer];
}
return self;
}
// Make custom configuration of your gradient here
- (void)configureGradientLayer {
CAGradientLayer *gLayer = (CAGradientLayer *)self.layer;
gLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor lightGrayColor] CGColor], nil];
}
#end
I've extended the accepted answer a little using Swift's extension functionality as well as an enum.
Oh and if you are using Storyboard like I do, make sure to call gradientBackground(from:to:direction:) in viewDidLayoutSubviews() or later.
Swift 3
enum GradientDirection {
case leftToRight
case rightToLeft
case topToBottom
case bottomToTop
}
extension UIView {
func gradientBackground(from color1: UIColor, to color2: UIColor, direction: GradientDirection) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [color1.cgColor, color2.cgColor]
switch direction {
case .leftToRight:
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
case .rightToLeft:
gradient.startPoint = CGPoint(x: 1.0, y: 0.5)
gradient.endPoint = CGPoint(x: 0.0, y: 0.5)
case .bottomToTop:
gradient.startPoint = CGPoint(x: 0.5, y: 1.0)
gradient.endPoint = CGPoint(x: 0.5, y: 0.0)
default:
break
}
self.layer.insertSublayer(gradient, at: 0)
}
}
Swift Implementation:
var gradientLayerView: UIView = UIView(frame: CGRectMake(0, 0, view.bounds.width, 50))
var gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = gradientLayerView.bounds
gradient.colors = [UIColor.grayColor().CGColor, UIColor.clearColor().CGColor]
gradientLayerView.layer.insertSublayer(gradient, atIndex: 0)
self.view.layer.insertSublayer(gradientLayerView.layer, atIndex: 0)
I have implemented this in swift with an extension:
Swift 3
extension UIView {
func addGradientWithColor(color: UIColor) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [UIColor.clear.cgColor, color.cgColor]
self.layer.insertSublayer(gradient, at: 0)
}
}
Swift 2.2
extension UIView {
func addGradientWithColor(color: UIColor) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [UIColor.clearColor().CGColor, color.CGColor]
self.layer.insertSublayer(gradient, atIndex: 0)
}
}
No I can set a gradient on every view like this:
myImageView.addGradientWithColor(UIColor.blue)
A Swift Approach
This answer builds on the answers above and provides implementation for dealing with the problem of the gradient not being properly applied during rotation. It satisfies this problem by changing the gradient layer to a square so that rotation in all directions results in a correct gradient. The function signature includes a Swift variadic argument that allows one to pass in as many CGColorRef's (CGColor) as needed (see sample usage). Also provided is an example as a Swift extension so that one can apply a gradient to any UIView.
func configureGradientBackground(colors:CGColorRef...){
let gradient: CAGradientLayer = CAGradientLayer()
let maxWidth = max(self.view.bounds.size.height,self.view.bounds.size.width)
let squareFrame = CGRect(origin: self.view.bounds.origin, size: CGSizeMake(maxWidth, maxWidth))
gradient.frame = squareFrame
gradient.colors = colors
view.layer.insertSublayer(gradient, atIndex: 0)
}
To use:
in viewDidLoad...
override func viewDidLoad() {
super.viewDidLoad()
configureGradientBackground(UIColor.redColor().CGColor, UIColor.whiteColor().CGColor)
}
Extension implementation
extension CALayer {
func configureGradientBackground(colors:CGColorRef...){
let gradient = CAGradientLayer()
let maxWidth = max(self.bounds.size.height,self.bounds.size.width)
let squareFrame = CGRect(origin: self.bounds.origin, size: CGSizeMake(maxWidth, maxWidth))
gradient.frame = squareFrame
gradient.colors = colors
self.insertSublayer(gradient, atIndex: 0)
}
}
Extension use-case example:
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.configureGradientBackground(UIColor.purpleColor().CGColor, UIColor.blueColor().CGColor, UIColor.whiteColor().CGColor)
}
Which means the gradient background can now be applied to any UIControl since all controls are UIViews (or a subclass) and all UIViews have CALayers.
Swift 4
Extension implementation
extension CALayer {
public func configureGradientBackground(_ colors:CGColor...){
let gradient = CAGradientLayer()
let maxWidth = max(self.bounds.size.height,self.bounds.size.width)
let squareFrame = CGRect(origin: self.bounds.origin, size: CGSize(width: maxWidth, height: maxWidth))
gradient.frame = squareFrame
gradient.colors = colors
self.insertSublayer(gradient, at: 0)
}
}
Extension use-case example:
override func viewDidLoad() {
super.viewDidLoad()
self.view.layer.configureGradientBackground(UIColor.purple.cgColor, UIColor.blue.cgColor, UIColor.white.cgColor)
}
What you are looking for is CAGradientLayer. Every UIView has a layer - into that layer you can add sublayers, just as you can add subviews. One specific type is the CAGradientLayer, where you give it an array of colors to gradiate between.
One example is this simple wrapper for a gradient view:
http://oleb.net/blog/2010/04/obgradientview-a-simple-uiview-wrapper-for-cagradientlayer/
Note that you need to include the QuartZCore framework in order to access all of the layer parts of a UIView.
Swift 4:
Shows gradient in IB correctly:
#IBDesignable public class GradientView: UIView {
override open class var layerClass: AnyClass {
return CAGradientLayer.classForCoder()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configureGradientLayer()
}
public override init(frame: CGRect) {
super.init(frame: frame)
configureGradientLayer()
}
func configureGradientLayer() {
let gradientLayer = layer as! CAGradientLayer
gradientLayer.colors = [UIColor(hex: 0x003399).cgColor, UIColor(hex: 0x00297b).cgColor]
}
}
Simple swift view based on Yuchen's version
class GradientView: UIView {
override class func layerClass() -> AnyClass { return CAGradientLayer.self }
lazy var gradientLayer: CAGradientLayer = {
return self.layer as! CAGradientLayer
}()
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
Then you can use gradientLayer after initialization like this...
someView.gradientLayer.colors = [UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor]
Its a good idea to call the solutions above to update layer on the
viewDidLayoutSubviews
to get the views updated correctly
My solution is to create UIView subclass with CAGradientLayer accessible as a readonly property. This will allow you to customize your gradient how you want and you don't need to handle layout changes yourself. Subclass implementation:
#interface GradientView : UIView
#property (nonatomic, readonly) CAGradientLayer *gradientLayer;
#end
#implementation GradientView
+ (Class)layerClass
{
return [CAGradientLayer class];
}
- (CAGradientLayer *)gradientLayer
{
return (CAGradientLayer *)self.layer;
}
#end
Usage:
self.iconBackground = [GradientView new];
[self.background addSubview:self.iconBackground];
self.iconBackground.gradientLayer.colors = #[(id)[UIColor blackColor].CGColor, (id)[UIColor whiteColor].CGColor];
self.iconBackground.gradientLayer.startPoint = CGPointMake(1.0f, 1.0f);
self.iconBackground.gradientLayer.endPoint = CGPointMake(0.0f, 0.0f);
SWIFT 3
To add a gradient layer on your view
Bind your view outlet
#IBOutlet var YOURVIEW : UIView!
Define the CAGradientLayer()
var gradient = CAGradientLayer()
Here is the code you have to write in your viewDidLoad
YOURVIEW.layoutIfNeeded()
gradient.startPoint = CGPoint(x: CGFloat(0), y: CGFloat(1))
gradient.endPoint = CGPoint(x: CGFloat(1), y: CGFloat(0))
gradient.frame = YOURVIEW.bounds
gradient.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
gradient.colors = [ UIColor(red: 255.0/255.0, green: 56.0/255.0, blue: 224.0/255.0, alpha: 1.0).cgColor,UIColor(red: 86.0/255.0, green: 13.0/255.0, blue: 232.0/255.0, alpha: 1.0).cgColor,UIColor(red: 16.0/255.0, green: 173.0/255.0, blue: 245.0/255.0, alpha: 1.0).cgColor]
gradient.locations = [0.0 ,0.6 ,1.0]
YOURVIEW.layer.insertSublayer(gradient, at: 0)
In Swift 3.1
I have added this extension to UIView
import Foundation
import UIKit
import CoreGraphics
extension UIView {
func gradientOfView(withColours: UIColor...) {
var cgColours = [CGColor]()
for colour in withColours {
cgColours.append(colour.cgColor)
}
let grad = CAGradientLayer()
grad.frame = self.bounds
grad.colors = cgColours
self.layer.insertSublayer(grad, at: 0)
}
}
which I then call with
class OverviewVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.gradientOfView(withColours: UIColor.red,UIColor.green, UIColor.blue)
}
}
I have implemented this in my code.
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, self.view.frame.size.width, 31.0f)];
view1.backgroundColor = [UIColor clearColor];
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view1.bounds;
UIColor *topColor = [UIColor colorWithRed:132.0/255.0 green:222.0/255.0 blue:109.0/255.0 alpha:1.0];
UIColor *bottomColor = [UIColor colorWithRed:31.0/255.0 green:150.0/255.0 blue:99.0/255.0 alpha:1.0];
gradient.colors = [NSArray arrayWithObjects:(id)[topColor CGColor], (id)[bottomColor CGColor], nil];
[view1.layer insertSublayer:gradient atIndex:0];
Now I can see a gradient on my view.
To give gradient color to UIView (swift 4.2)
func makeGradientLayer(`for` object : UIView, startPoint : CGPoint, endPoint : CGPoint, gradientColors : [Any]) -> CAGradientLayer {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = gradientColors
gradient.locations = [0.0 , 1.0]
gradient.startPoint = startPoint
gradient.endPoint = endPoint
gradient.frame = CGRect(x: 0, y: 0, w: object.frame.size.width, h: 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 = makeGradientLayer(for: cell, startPoint: start, endPoint: end, gradientColors: [
UIColor(red:0.92, green:0.07, blue:0.4, alpha:1).cgColor,
UIColor(red:0.93, green:0.11, blue:0.14, alpha:1).cgColor
])
self.vwTemp.layer.insertSublayer(gradient, at: 0)
This framework is good for gradients and other UI modifications in Historyboard: Design and prototype customized UI, interaction, navigation, transition and animation for App Store ready Apps in Interface Builder with IBAnimatable.
With this you can select a view, set the class to AnimatableView, and from Interface Builder property set gradients and preview results in realtime.
See this to know how apply gradients to UIView.
Related
Swift PageViewController gradient background
I have a PageViewController and need to set a gradient background to it. When using gradient background in normal ViewControllers, I use the following: let layer = self.layer as! CAGradientLayer layer.colors = [FirstColor.cgColor, SecondColor.cgColor] layer.locations = [0.5] when I try to use self.view.layer inside my PageViewController I get an error: Could not cast value of type 'CALayer' (0x104f57900) to 'CAGradientLayer' Can someone help me setting the background of a page view controller to an gradient? Thanks
Hi you can create a UIView extension and just call like this: view.applyGradient(colours: [UIColor.red, UIColor.green]) here the extension code extension UIView { func applyGradient(colours: [UIColor]) -> Void { clipsToBounds = true let gradient: CAGradientLayer = CAGradientLayer() gradient.frame = self.bounds gradient.colors = colours.map { $0.cgColor } gradient.startPoint = CGPoint(x: 0, y: 0) gradient.endPoint = CGPoint(x: 1, y: 0) layer.insertSublayer(gradient, at: 0) } } with this code you can apply to UIButtons, UILabels, anything that extends from UIView hope it helps
How can I set the UINavigationbar with gradient color?
I want to set the UINavigationbar backgroundColor to a gradient color where I would like to set it via an array of colors to create a Gradient, ideally, as accessible methods inside UINavigationBar to change its color to this gradient. Any suggestions? (Aside from setting an image manually as the background image of the navigation bar)
Details Xcode 11.4 (11E146), swift 5 Tested on iOS 13.1, 12.2, 11.0.1 Solution class UINavigationBarGradientView: UIView { enum Point { case topRight, topLeft case bottomRight, bottomLeft case custom(point: CGPoint) var point: CGPoint { switch self { case .topRight: return CGPoint(x: 1, y: 0) case .topLeft: return CGPoint(x: 0, y: 0) case .bottomRight: return CGPoint(x: 1, y: 1) case .bottomLeft: return CGPoint(x: 0, y: 1) case .custom(let point): return point } } } private weak var gradientLayer: CAGradientLayer! convenience init(colors: [UIColor], startPoint: Point = .topLeft, endPoint: Point = .bottomLeft, locations: [NSNumber] = [0, 1]) { self.init(frame: .zero) let gradientLayer = CAGradientLayer() gradientLayer.frame = frame layer.addSublayer(gradientLayer) self.gradientLayer = gradientLayer set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations) backgroundColor = .clear } func set(colors: [UIColor], startPoint: Point = .topLeft, endPoint: Point = .bottomLeft, locations: [NSNumber] = [0, 1]) { gradientLayer.colors = colors.map { $0.cgColor } gradientLayer.startPoint = startPoint.point gradientLayer.endPoint = endPoint.point gradientLayer.locations = locations } func setupConstraints() { guard let parentView = superview else { return } translatesAutoresizingMaskIntoConstraints = false topAnchor.constraint(equalTo: parentView.topAnchor).isActive = true leftAnchor.constraint(equalTo: parentView.leftAnchor).isActive = true parentView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true parentView.rightAnchor.constraint(equalTo: rightAnchor).isActive = true } override func layoutSubviews() { super.layoutSubviews() guard let gradientLayer = gradientLayer else { return } gradientLayer.frame = frame superview?.addSubview(self) } } extension UINavigationBar { func setGradientBackground(colors: [UIColor], startPoint: UINavigationBarGradientView.Point = .topLeft, endPoint: UINavigationBarGradientView.Point = .bottomLeft, locations: [NSNumber] = [0, 1]) { guard let backgroundView = value(forKey: "backgroundView") as? UIView else { return } guard let gradientView = backgroundView.subviews.first(where: { $0 is UINavigationBarGradientView }) as? UINavigationBarGradientView else { let gradientView = UINavigationBarGradientView(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations) backgroundView.addSubview(gradientView) gradientView.setupConstraints() return } gradientView.set(colors: colors, startPoint: startPoint, endPoint: endPoint, locations: locations) } } Usage navigationBar.setGradientBackground(colors: [.lightGray, .red], startPoint: .topLeft, endPoint: .bottomRight)
Create gradient layer and add it as background of navigation bar. CAGradientLayer *gradient = [CAGradientLayer layer]; gradient.frame = self.navigationController.navigationBar.bounds; gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor whiteColor] CGColor], (id)[[UIColor blackColor] CGColor], nil]; [self.navigationController.navigationBar setBackgroundImage:[self imageFromLayer:gradient] forBarMetrics:UIBarMetricsDefault]; For creating image from layer. - (UIImage *)imageFromLayer:(CALayer *)layer { UIGraphicsBeginImageContext([layer frame].size); [layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return outputImage; } One more thing, there is one library available in github : CRGradientNavigationBar you can also use this library.
In Swift 3, Swift 4 and Swift 5: let gradient = CAGradientLayer() let sizeLength = UIScreen.main.bounds.size.height * 2 let defaultNavigationBarFrame = CGRect(x: 0, y: 0, width: sizeLength, height: 64) gradient.frame = defaultNavigationBarFrame gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor] UINavigationBar.appearance().setBackgroundImage(self.image(fromLayer: gradient), for: .default) For creating image from layer: func image(fromLayer layer: CALayer) -> UIImage { UIGraphicsBeginImageContext(layer.frame.size) layer.render(in: UIGraphicsGetCurrentContext()!) let outputImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return outputImage! } In Swift 2: let gradient = CAGradientLayer() let sizeLength = UIScreen.mainScreen().bounds.size.height * 2 let defaultNavigationBarFrame = CGRectMake(0, 0, sizeLength, 64) gradient.frame = defaultNavigationBarFrame gradient.colors = [UIColor.whiteColor().CGColor, UIColor.blackColor().CGColor] UINavigationBar.appearance().setBackgroundImage(self.image(fromLayer: gradient), forBarMetrics: .Default) For creating image from layer: func image(fromLayer layer: CALayer) -> UIImage { UIGraphicsBeginImageContext(layer.frame.size) layer.renderInContext(UIGraphicsGetCurrentContext()!) let outputImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return outputImage! }
This is the solution without using an intermediate CAGradientLayer, and just using CoreGraphics, in Swift 3.0. Essentially, the method creates a UIImage on the fly with the gradient colors passed and sets it. extension UINavigationBar { /// Applies a background gradient with the given colors func apply(gradient colors : [UIColor]) { var frameAndStatusBar: CGRect = self.bounds frameAndStatusBar.size.height += 20 // add 20 to account for the status bar setBackgroundImage(UINavigationBar.gradient(size: frameAndStatusBar.size, colors: colors), for: .default) } /// Creates a gradient image with the given settings static func gradient(size : CGSize, colors : [UIColor]) -> UIImage? { // Turn the colors into CGColors let cgcolors = colors.map { $0.cgColor } // Begin the graphics context UIGraphicsBeginImageContextWithOptions(size, true, 0.0) // If no context was retrieved, then it failed guard let context = UIGraphicsGetCurrentContext() else { return nil } // From now on, the context gets ended if any return happens defer { UIGraphicsEndImageContext() } // Create the Coregraphics gradient var locations : [CGFloat] = [0.0, 1.0] guard let gradient = CGGradient(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: cgcolors as NSArray as CFArray, locations: &locations) else { return nil } // Draw the gradient context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: 0.0), options: []) // Generate the image (the defer takes care of closing the context) return UIGraphicsGetImageFromCurrentImageContext() } } The defer statement makes this so much clean than prior versions. Be noted that CGGradient are available since iOS 8.0. Also, this creates the gradient from left to right, tweaking the parameters of drawLinearGradient (start and end) moves the locations. This is up for your implementation.
For Swift 4.2 extension UINavigationBar { func setGradientBackground(colors: [Any]) { let gradient: CAGradientLayer = CAGradientLayer() gradient.locations = [0.0 , 0.5, 1.0] gradient.startPoint = CGPoint(x: 0.0, y: 1.0) gradient.endPoint = CGPoint(x: 1.0, y: 1.0) var updatedFrame = self.bounds updatedFrame.size.height += self.frame.origin.y gradient.frame = updatedFrame gradient.colors = colors; self.setBackgroundImage(self.image(fromLayer: gradient), for: .default) } func image(fromLayer layer: CALayer) -> UIImage { UIGraphicsBeginImageContext(layer.frame.size) layer.render(in: UIGraphicsGetCurrentContext()!) let outputImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return outputImage! } } How to use self.navigationController?.navigationBar.setGradientBackground(colors: [ UIColor.red.cgColor, UIColor.green.cgColor, UIColor.blue.cgColor ])
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) } }
SWIFT 5 To make the horizontal gradient background of the navigation bar along with status bar too.. just put this code in your viewcontroller's viewDidLoad() method. self.navigationItem.title = "Gradiant Back Ground" let gradientLayer = CAGradientLayer() var updatedFrame = self.navigationController!.navigationBar.bounds updatedFrame.size.height += UIApplication.shared.statusBarFrame.size.height gradientLayer.frame = updatedFrame gradientLayer.colors = [UIColor.green.cgColor, UIColor.blue.cgColor] // start color and end color gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0) // Horizontal gradient start gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.0) // Horizontal gradient end UIGraphicsBeginImageContext(gradientLayer.bounds.size) gradientLayer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() self.navigationController!.navigationBar.setBackgroundImage(image, for: UIBarMetrics.default) output Gradient will look like this.
In Swift 3 let gradient = CAGradientLayer() let sizeLength = UIScreen.main.bounds.size.height * 2 let defaultNavigationBarFrame = CGRect(x: 0, y: 0, width: sizeLength, height: 64) gradient.frame = defaultNavigationBarFrame gradient.colors = [UIColor.white.cgColor, UIColor.black.cgColor] UINavigationBar.appearance().setBackgroundImage(self.image(fromLayer: gradient), for: .default) func image(fromLayer layer: CALayer) -> UIImage { UIGraphicsBeginImageContext(layer.frame.size) layer.render(in: UIGraphicsGetCurrentContext()!) let outputImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return outputImage! }
Objective C solution that works on iPhone X as well: - (void)addGradientToNavigationBar { CAGradientLayer *gradient = [CAGradientLayer layer]; CGRect gradientFrame = self.navigationController.navigationBar.bounds; gradientFrame.size.height += [UIApplication sharedApplication].statusBarFrame.size.height; gradient.frame = gradientFrame; gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor colorGradientUp] CGColor], (id)[[UIColor colorGradientDown] CGColor], nil]; [self.navigationController.navigationBar setBackgroundImage:[self imageFromLayer:gradient] forBarMetrics:UIBarMetricsDefault]; } - (UIImage *)imageFromLayer:(CALayer *)layer { UIGraphicsBeginImageContext([layer frame].size); [layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return outputImage; }
This amazing tutorial from Lawrence Tan shows how to set a gradient using barTintColor and no backgroundImage: https://medium.com/swift2go/add-gradient-to-navigation-bar-in-swift-9284fe91fea2 Summary CAGradientLayer extension extension CAGradientLayer { class func primaryGradient(on view: UIView) -> UIImage? { let gradient = CAGradientLayer() let flareRed = UIColor(displayP3Red: 241.0/255.0, green: 39.0/255.0, blue: 17.0/255.0, alpha: 1.0) let flareOrange = UIColor(displayP3Red: 245.0/255.0, green: 175.0/255.0, blue: 25.0/255.0, alpha: 1.0) var bounds = view.bounds bounds.size.height += UIApplication.shared.statusBarFrame.size.height gradient.frame = bounds gradient.colors = [flareRed.cgColor, flareOrange.cgColor] gradient.startPoint = CGPoint(x: 0, y: 0) gradient.endPoint = CGPoint(x: 1, y: 0) return gradient.createGradientImage(on: view) } private func createGradientImage(on view: UIView) -> UIImage? { var gradientImage: UIImage? UIGraphicsBeginImageContext(view.frame.size) if let context = UIGraphicsGetCurrentContext() { render(in: context) gradientImage = UIGraphicsGetImageFromCurrentImageContext()?.resizableImage(withCapInsets: UIEdgeInsets.zero, resizingMode: .stretch) } UIGraphicsEndImageContext() return gradientImage } } Apply the gradient guard let navigationController = navigationController, let flareGradientImage = CAGradientLayer.primaryGradient(on: navigationController.navigationBar) else { print("Error creating gradient color!") return } navigationController.navigationBar.barTintColor = UIColor(patternImage: flareGradientImage)
This is a framework for different UI components gradient include UINavigation bar: enter link description here first : then in your root ViewController that it embedded into an UINavigationController import SHNDStuffs put this in your ViewDidLoad() class RootViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() SHNDNavigationBarGradient(firstColor: .darkGray, secondColor: .white, tintColor: .black, isHorizontal: true) }
Set a simple color gradient as the background of UIToolbar
I can easily change the UIToolbar background color thanks to the Bart Tint property in Interface Builder. However, I would like to use a simple, two-color vertical gradient as background. The solution should work with any custom toolbar height, and my target is iOS 7+. If it must be done programmatically, both Objective-C and Swift are welcome. Edit: based on the answers I'm doing this in viewDidLoad, but it makes no effect: var gradient = CAGradientLayer() gradient.frame = toolbar.bounds gradient.colors = [UIColor.redColor(), UIColor.greenColor()] toolbar.layer.insertSublayer(gradient, atIndex: 0) Edit 2: based on the answers, I replaced the following line, but it still makes no effect: gradient.colors = [UIColor.redColor().CGColor, UIColor.greenColor().CGColor] Edit 3: I got it. I had to set "Bar Tint" to "Default" in Interface Builder first, so that the tint is transparent and doesn't hide the programmatically added gradient layer.
You can use CAGradientLayer for achieving this. Objective C: CAGradientLayer *gradient = [CAGradientLayer layer]; gradient.frame = yourToolbar.bounds; gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor], (id)[[UIColor greenColor] CGColor], nil]; [yourToolbar.layer insertSublayer:gradient atIndex:0]; Swift: var gradient:CAGradientLayer = CAGradientLayer() gradient.frame = yourToolbar.bounds gradient.colors = [UIColor.redColor().CGColor, UIColor.greenColor().CGColor] yourToolbar.layer.insertSublayer(gradient, atIndex: 0)
This works var gradient:CAGradientLayer = CAGradientLayer() gradient.frame = self.toolbar.bounds gradient.colors = [UIColor.redColor().CGColor, UIColor.greenColor().CGColor] self.toolbar.layer.insertSublayer(gradient, atIndex: 0)
It's the same way as setting a Gradient Background for UINavigationBar - you can use the setBackgroundImage method of UIToolbar. Just add these two extensions: extension UIImage { static func imageFromLayer (layer: CALayer) -> UIImage? { UIGraphicsBeginImageContext(layer.frame.size) guard let currentContext = UIGraphicsGetCurrentContext() else {return nil} layer.render(in: currentContext) let outputImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return outputImage } } extension UIToolbar { func setGradientBackground(barFrame: CGRect, aColor: UIColor, bColor: UIColor, aX: CGFloat, bX: CGFloat, aY: CGFloat, bY: CGFloat) { let gradientLayer = CAGradientLayer() gradientLayer.frame = frame gradientLayer.colors = [aColor.cgColor, bColor.cgColor] gradientLayer.locations = [0.0, 1.0] gradientLayer.startPoint = CGPoint(x: aX, y: aY) gradientLayer.endPoint = CGPoint(x: bX, y: bY) if let image = UIImage.imageFromLayer(layer: gradientLayer) { self.setBackgroundImage(image, forToolbarPosition: .bottom, barMetrics: .default) } } } This creates a UIImage from a CAGradientLayer, and subsequently sets it as a background image of your UIToolbar. Then just apply it to your UIToolbar. For instance, a horizontal gradient from blue to white: myToolbar.setGradientBackground( barFrame: navigationBar.frame, aColor: UIColor.blue, bColor: UIColor.white, aX: 0.0, bX: 1.0, aY: 0.5, bY: 0.5 ) Hope this helps!
// Use this Code: i=2; leftColor = [UIColor colorWithRed:0.03 green:0.20 blue:0.95 alpha:1.0]; rightColor = [UIColor colorWithRed:0.98 green:0.00 blue:0.89 alpha:1.0]; gradient.frame = self.font_imageview.bounds; gradient.colors = [NSArray arrayWithObjects: (id)[rightColor CGColor], (id)[UIColor clearColor].CGColor, (id)[leftColor CGColor],nil]; [font_imageview.layer insertSublayer:gradient atIndex:0]; gradient.startPoint = CGPointMake(0.0, 0.5); gradient.endPoint = CGPointMake(1.0, 0.5);}
If you are using custom toolbar then maybe you face the size problem. view_toolbar can be any view, Here I found another perfect solution: let gradientLayer = CAGradientLayer() var bounds = self.view_toolbar.bounds bounds.size.height += UIApplication.shared.statusBarFrame.size.height gradientLayer.frame = bounds gradientLayer.colors = [colorPrimary, colorPrimaryDark] gradientLayer.startPoint = CGPoint(x: 0, y: 0) gradientLayer.endPoint = CGPoint(x: 1, y: 0) self.view_toolbar.layer.insertSublayer(gradientLayer, at: 0)
How could I set gradient fixed background on UICollectionView?
I used following code. CAGradientLayer* collectionRadient = [CAGradientLayer layer]; collectionRadient.bounds = self.collectionView.bounds; collectionRadient.anchorPoint = CGPointZero; collectionRadient.colors = [NSArray arrayWithObjects:(id)[startColor CGColor],(id)[endColor CGColor], nil]; [self.collectionView.layer insertSublayer:collectionRadient atIndex:0]; But it drawn on cells included images. so cell was not shown. I want to draw gradient background of UICollectionView under Cells and fixed it when view scrolled. Let me know Please.
Try this... You have to assign a view to use background view. CAGradientLayer* collectionGradient = [CAGradientLayer layer]; collectionGradient.bounds = self.view.bounds; collectionGradient.anchorPoint = CGPointZero; collectionGradient.colors = [NSArray arrayWithObjects:(id)[[UIColor redColor] CGColor],(id)[[UIColor greenColor] CGColor], nil]; UIView *vv = [[UIView alloc] init]; vview.backgroundView = vv; [vview.backgroundView.layer insertSublayer:collectionGradient atIndex:0];
In Swift 3.0 I like to start with a custom class for gradients import UIKit #IBDesignable class GradientView: UIView { //set your start color #IBInspectable var startColor: UIColor = UIColor.black { didSet { updateColors() }} //set your end color #IBInspectable var endColor: UIColor = UIColor.white { didSet { updateColors() }} //you can change anchors and directions #IBInspectable var startLocation: Double = 0.05 { didSet { updateLocations() }} #IBInspectable var endLocation: Double = 0.95 { didSet { updateLocations() }} #IBInspectable var horizontalMode: Bool = false { didSet { updatePoints() }} #IBInspectable var diagonalMode: Bool = false { didSet { updatePoints() }} override class var layerClass: AnyClass { return CAGradientLayer.self } var gradientLayer: CAGradientLayer { return layer as! CAGradientLayer } func updatePoints() { if horizontalMode { gradientLayer.startPoint = diagonalMode ? CGPoint(x: 1, y: 0) : CGPoint(x: 0, y: 0.5) gradientLayer.endPoint = diagonalMode ? CGPoint(x: 0, y: 1) : CGPoint(x: 1, y: 0.5) } else { gradientLayer.startPoint = diagonalMode ? CGPoint(x: 0, y: 0) : CGPoint(x: 0.5, y: 0) gradientLayer.endPoint = diagonalMode ? CGPoint(x: 1, y: 1) : CGPoint(x: 0.5, y: 1) } } func updateLocations() { gradientLayer.locations = [startLocation as NSNumber, endLocation as NSNumber] } func updateColors() { gradientLayer.colors = [startColor.cgColor, endColor.cgColor] } override func layoutSubviews() { super.layoutSubviews() updatePoints() updateLocations() updateColors() } } With the custom class somewhere in the project and added to the target you just need to do as mentioned earlier and add it as a background view rather than background color or separate normal view sent to the back Implement like so: let gradient = GradientView() gradient.frame = self.view.bounds //add to your collectionView collectionView?.addSubview(gradient) collectionView?.sendSubview(toBack: gradient) self.collectionView?.backgroundView = gradient
Directions Create a view Assign that view to the backgroundView of you collectionView Create a gradient Add gradient layer to collectionView backgroundView Code Swift 4.2 private func addBackgroundGradient() { let collectionViewBackgroundView = UIView() let gradientLayer = CAGradientLayer() gradientLayer.frame.size = view.frame.size // Start and end for left to right gradient gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5) gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5) gradientLayer.colors = [UIColor.blue.cgColor, UIColor.green.cgColor] collectionView.backgroundView = collectionViewBackgroundView collectionView.backgroundView?.layer.addSublayer(gradientLayer) } If you want a top to bottom gradient all you have to do is remove endPoint and startPoint. You can read more about gradients here. Note that startPoint and endPoint are defined in the coordinate plane from (0.0, 0.0) to (1.0, 1.0)
UIProgressView with gradient
Is there a way to make progressTintColor make a gradient one? Specifically I would like to make it a gradient color from green to red - standard temperature mode. Adding sublayer didn't work as it put the layer "behind" the view: CAGradientLayer *gradient = [CAGradientLayer layer]; gradient.frame = self.testBar.bounds; gradient.colors = [NSArray arrayWithObjects:(id)[[UIColor greenColor] CGColor], (id)[[UIColor redColor] CGColor], nil]; [self.testBar.layer insertSublayer:gradient atIndex:0];
You could try creating patterned UIColor and set that as the tint color, but I doubt it would work. Otherwise you will probably need to subclass UIProgressView or build your own UIView subclass. A progress bar isn't that difficult to draw.
I found this and it works very well in my local https://medium.com/academy-poa/how-to-create-a-uiprogressview-with-gradient-progress-in-swift-2d1fa7d26f24 1.Extent UIImage extension UIImage { public convenience init?(bounds: CGRect, colors: [UIColor], orientation: GradientOrientation = .horizontal) { let gradientLayer = CAGradientLayer() gradientLayer.frame = bounds gradientLayer.colors = colors.map({ $0.cgColor }) if orientation == .horizontal { gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5); gradientLayer.endPoint = CGPoint(x: 1.0, y: 0.5); } UIGraphicsBeginImageContext(gradientLayer.bounds.size) gradientLayer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() guard let cgImage = image?.cgImage else { return nil } self.init(cgImage: cgImage) } } public enum GradientOrientation { case vertical case horizontal } create your own gradient progress bar import UIKit class GradientProgressView: UIProgressView { #IBInspectable var firstColor: UIColor = UIColor.white { didSet { updateView() } } #IBInspectable var secondColor: UIColor = UIColor.black { didSet { updateView() } } func updateView() { if let gradientImage = UIImage(bounds: self.bounds, colors: [firstColor, secondColor]) { self.progressImage = gradientImage } } } in your class let progressView = GradientProgressView(progressViewStyle: .bar) progressView.firstColor = UIColor(red: 0.949, green: 0.478, blue: 0.329, alpha: 1) //your own color progressView.secondColor = UIColor(red: 0.631, green: 0.329, blue: 0.949, alpha: 1) //your own color progressView.updateView()