I am new to iOS.I want to apply gradient colors on CarbonTabSwipeNavigation.I tried to apply the gradient to the toolbar of CarbonTabSwipeNavigation, but it is not working. Here's the code.
let carbonTabSwipeNavigation = CarbonTabSwipeNavigation(items: items, delegate: self)
carbonTabSwipeNavigation.toolbar.setGradientToToolbar(default_pri_color: UIColor.DarkBlue(), default_sec_color: UIColor.LightBlue())
Here's the Function
extension UIToolbar {
func setGradientToToolbar(default_pri_color: UIColor, default_sec_color: UIColor) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [default_pri_color.cgColor, default_sec_color.cgColor]
self.layer.insertSublayer(gradient, at: 0)
}
}
The issue is with the gradientLayer frame. Toolbar constraints are not yet laid out when you are calling setGradientToToolbar method on it. So, with everything zero in bounds you are not able to see the gradient layer.
There are two ways to fix this,
1) You provide frame for gradientLayer as below,
func setGradientToToolbar(default_pri_color: UIColor , default_sec_color:UIColor) {
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 120)
gradient.colors = [UIColor.red.cgColor, UIColor.green.cgColor]
self.layer.insertSublayer(gradient, at: 0)
}
2) Call setGradientToToolbar method on toolbar after your viewController finishes layout for all views. Below is the complete example,
import UIKit
import CarbonKit
class ViewController: UIViewController, CarbonTabSwipeNavigationDelegate {
var carbonTabSwipeNavigation: CarbonTabSwipeNavigation!
override func viewDidLoad() {
super.viewDidLoad()
let items = ["Features", "Products", "About"]
carbonTabSwipeNavigation = CarbonTabSwipeNavigation(items: items, delegate: self)
carbonTabSwipeNavigation.toolbarHeight.constant = 120
carbonTabSwipeNavigation.insert(intoRootViewController: self)
}
func carbonTabSwipeNavigation(_ carbonTabSwipeNavigation: CarbonTabSwipeNavigation, viewControllerAt index: UInt) -> UIViewController {
return UIViewController()
}
override func viewDidLayoutSubviews() {
carbonTabSwipeNavigation.toolbar.setGradientToToolbar(default_pri_color: .red, default_sec_color: .yellow)
}
}
extension UIToolbar {
func setGradientToToolbar(default_pri_color: UIColor , default_sec_color:UIColor) {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [default_pri_color.cgColor, default_sec_color.cgColor]
self.layer.insertSublayer(gradient, at: 0)
}
}
Both will give you the below result,
Related
This question already has answers here:
How to Apply Gradient to background view of iOS Swift App
(30 answers)
Closed 3 years ago.
I am trying to set the background on my UIView to a gradient. I have the following in my UIViewController's viewDidLoad method:
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.blue70.cgColor, UIColor.blue60.cgColor, UIColor.blue70.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1 )
gradientLayer.frame = self.view.bounds
self.view.layer.insertSublayer(gradientLayer, at: 0)
The gradient renders as expected. However, when I rotate the device the gradient does not redraw and is no longer rendered properly. Rotating from portrait->landscape leaves me with a blank section on the right or left. Rotating from landscape->portrait leaves me with a blank section at the bottom. I tried moving this code to viewDidLayoutSubviews, but that didn't solve it. Any recommendations on how to accomplish this?
What i would recommend here is to add the gradient in the viewWillLayoutSubviews() method
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
self.view.addGradiant()
}
But now we would have to remove the old layer When adding the new one with the new frames(In case the Phone rotates)
We can then first remove the layer and add the new one like in this extension method
extension UIView {
func addGradiant() {
let GradientLayerName = "gradientLayer"
if let oldlayer = self.layer.sublayers?.filter({$0.name == GradientLayerName}).first {
oldlayer.removeFromSuperlayer()
}
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor.blue.cgColor, UIColor.red.cgColor, UIColor.green.cgColor]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 1 )
gradientLayer.frame = self.bounds
gradientLayer.name = GradientLayerName
self.layer.insertSublayer(gradientLayer, at: 0)
}
}
Make gradientLayer view controller's property.
let gradientLayer = CAGradientLayer()
Add gradientLayer in the viewDidLoad in the same way you did.
Set the frame in viewWillLayoutSubviews
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
gradientLayer.frame = self.view.bounds
}
While you can do as the other answers suggest and update your gradient layer bounds in viewDidLayoutSubviews, the more reusable way to do things is to make a gradient view subclass who's layerClass is a CAGradientLayer. Such a view will have its layer automatically resized to fit the view (because the gradient layer is now the backing layer for the view and not some subLayer) and you can use this view anywhere just by changing the class of your view in your nib file or storyboard:
class GradientView: UIView {
var colors: [UIColor] = [.red, .white] {
didSet {
setup()
}
}
var locations: [CGFloat] = [0,1] {
didSet {
setup()
}
}
var startPoint: CGPoint = .zero {
didSet {
setup()
}
}
var endPoint: CGPoint = CGPoint(x: 1, y: 1) {
didSet {
setup()
}
}
override class var layerClass : AnyClass { return CAGradientLayer.self }
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func awakeFromNib() {
super.awakeFromNib()
setup()
}
private func setup() {
guard let layer = self.layer as? CAGradientLayer else {
return
}
layer.colors = colors.map { $0.cgColor }
layer.locations = locations.map { NSNumber(value: Double($0)) }
layer.startPoint = startPoint
layer.endPoint = endPoint
}
}
I am new to iOS.I want to apply gradient colors on CarbonTabSwipeNavigation.I tried to apply the gradient to the toolbar of CarbonTabSwipeNavigation, but it is not happening
I have tried with Static Colore Code
carbonTabSwipeNavigation.toolbar.isTranslucent = true
var color1 = hexStringToUIColor(hex: "#00275E")
carbonTabSwipeNavigation.carbonSegmentedControl?.backgroundColor = color1
carbonTabSwipeNavigation.setIndicatorColor(UIColor.white) //tabBar font
carbonTabSwipeNavigation.setSelectedColor(UIColor.white, font: UIFont.boldSystemFont(ofSize: 14))
First, add these extensions in your project.
extension UINavigationBar {
func setGradientBackground(colors: [UIColor]) {
var updatedFrame = bounds
updatedFrame.size.height += self.frame.origin.y
let gradientLayer = CAGradientLayer(frame: updatedFrame, colors: colors)
setBackgroundImage(gradientLayer.createGradientImage(), for: UIBarMetrics.default)
}
}
extension UIView {
func setGradientBackgroundOnView(colors: [UIColor]) {
var updatedFrame = bounds
// updatedFrame.size.height += self.frame.origin.y
let gradientLayer = CAGradientLayer(frame: updatedFrame, colors: colors)
self.layer.insertSublayer(gradientLayer, at: 0)
}
}
extension CAGradientLayer {
convenience init(frame: CGRect, colors: [UIColor]) {
self.init()
self.frame = frame
self.colors = []
for color in colors {
self.colors?.append(color.cgColor)
}
startPoint = CGPoint(x: 0, y: 1)
endPoint = CGPoint(x: 1, y: 1)
}
func createGradientImage() -> UIImage? {
var image: UIImage? = nil
UIGraphicsBeginImageContext(bounds.size)
if let context = UIGraphicsGetCurrentContext() {
render(in: context)
image = UIGraphicsGetImageFromCurrentImageContext()
}
UIGraphicsEndImageContext()
return image
}
}
Then call this method as your requirement.
Example:- If you want to set a gradient color on navigationbar of your app then call use this
self.navigationController?.navigationBar.setGradientBackground(colors: [UIColor.black, UIColor.red])
I'm trying to apply a gradient to a view which is constraint to the top, left and right of the main screen but for some reason the gradient doesn't cover the whole width of the view that is applied to (see the yellow in the picture).
class ViewController: UIViewController {
#IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let gradient = CAGradientLayer()
gradient.colors = [UIColor.blue.cgColor, UIColor.white.cgColor]
gradient.startPoint = CGPoint(x:00, y:00)
gradient.endPoint = CGPoint(x:0, y:0.6)
gradient.frame = myView.bounds
myView.clipsToBounds = true
myView.layer.insertSublayer(gradient, at: 0)
}
}
What am I doing wrong?
The problem is likely that you are adding the gradient layer in viewDidLoad(). A view doesn't get set to it's final size until after viewDidLoad().
Your view controller may be set up in your XIB/Storyboard for a different screen-size than you're running it on. (Say you have it set to iPhone SE size, but you're running it on a 6. The 6's screen is a little wider, so the layer will get set to the width of the iPhone SE, when the view is first loaded. Then the view will be resized, but the layer's size will never be adjusted.)
You should implement the UIViewController method viewDidLayoutSubviews(), and in that method, adjust the layer's frame:
override func viewDidLayoutSubviews() {
gradientLayer.frame = self.view.bounds
}
That way if the view gets resized (due to auto-rotation, for example) the gradient layer will be automatically adjusted accordingly.
EDIT:
As pointed out by Sulthan, changes to a layer's frame are animated by default. You should probably wrap the frame change in a CATransaction.begin/CATransaction.end and disable actions, like below:
override func viewDidLayoutSubviews() {
CATransaction.begin()
CATransaction.setDisableActions(true)
gradientLayer.frame = self.view.bounds
CATransaction.commit()
}
You can turn it to a UIView. So it will resize automatically and can be seen directly in the Storyboard:
#IBDesignable
final class GradientView: UIView {
#IBInspectable var firstColor: UIColor = .clear { didSet { updateView() } }
#IBInspectable var secondColor: UIColor = .clear { didSet { updateView() } }
#IBInspectable var startPoint: CGPoint = CGPoint(x: 0, y: 0) { didSet { updateView() } }
#IBInspectable var endPoint: CGPoint = CGPoint(x: 1, y: 1) { didSet { updateView() } }
override class var layerClass: AnyClass { get { CAGradientLayer.self } }
override func layoutSubviews() {
super.layoutSubviews()
updateView()
layer.frame = bounds
}
private func updateView() {
let layer = self.layer as! CAGradientLayer
layer.colors = [firstColor, secondColor].map {$0.cgColor}
layer.startPoint = startPoint
layer.endPoint = endPoint
}
}
You needn't set the start and end point, given your goal is to have the gradient span the entire view. You're already setting that with `
gradientLayer.frame = self.view.bounds
`
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myView: UIView!
var gradientLayer: CAGradientLayer!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
createGradientLayer()
}
func createGradientLayer() {
gradientLayer = CAGradientLayer()
gradientLayer.frame = self.view.bounds
gradientLayer.colors = [UIColor.blueColor().CGColor, UIColor.whiteColor().CGColor]
self.view.layer.addSublayer(gradientLayer)
}
}
simply put you gredient layer in main thred like this
DispatchQueue.main.async {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = self.viewGredient.bounds
gradientLayer.colors = [UIColor.black.cgColor, UIColor.white.cgColor]
gradientLayer.locations = [0.0, 1.0]
self.viewGredient.layer.addSublayer(gradientLayer)
}
You can simplify it by CustomClass with one line of code
class GradientView: UIView {
override class var layerClass: Swift.AnyClass {
return CAGradientLayer.self
}
}
// MARK: Usage
#IBOutlet weak var myView: GradientView!
guard let gradientLayer = myView.layer as? CAGradientLayer else { return }
gradient.colors = [UIColor.blue.cgColor, UIColor.white.cgColor]
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x:0, y:0.6)
I have a UIViewController that is also a UIScrollViewDelegate
The UIViewController has multiple slides that are UIViews and for each slide I want to set a different gradient background color programmatically, however it does not seem to be working as no gradient is being set.
My UIView extension
import UIKit
extension UIView {
func addGradient(){
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [UIColor.black, UIColor.orange]
let backgroundIndex:UInt32 = 0;
self.layer.insertSublayer(gradient, at: backgroundIndex)
}
}
My FirstSlide that has it's own FirstSlide.xib
import UIKit
class FirstSlide: UIView {
}
In my WelcomeSliderViewController
import UIKit
class WelcomeSliderViewController: UIViewController, UIScrollViewDelegate{
#IBOutlet weak var slideScrollView: UIScrollView!
#IBOutlet weak var pagerControl: UIPageControl!
override func viewDidLoad() {
super.viewDidLoad()
slideScrollView.delegate = self
let slides = createSlides()
setupSliderView(slides: slides)
pagerControl.numberOfPages = slides.count
pagerControl.currentPage = 0
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func pagerControlClicked(_ sender: Any) {
self.slideScrollView
.scrollRectToVisible(CGRect(
x: Int(self.slideScrollView.frame.size.width) * self.pagerControl.currentPage,
y: 0,
width:Int(self.slideScrollView.frame.size.width),
height: Int(self.slideScrollView.frame.size.height)),
animated: true)
}
func createSlides() -> [UIView] {
let firstSlide: FirstSlide = Bundle.main.loadNibNamed("FirstSlide", owner:self , options: nil)?.first as! FirstSlide
let secondSlide: SecondSlide = Bundle.main.loadNibNamed("SecondSlide", owner:self , options: nil)?.first as! SecondSlide
firstSlide.addGradient()
return [firstSlide, SecondSlide]
}
func setupSliderView(slides:[UIView]) {
slideScrollView.frame = CGRect(x: 0, y:0, width: view.frame.width, height: view.frame.height)
slideScrollView.contentSize = CGSize(width: view.frame.width * CGFloat(slides.count), height: view.frame.height)
for i in 0 ..< slides.count{
slides[i].frame = CGRect(x: view.frame.width * CGFloat(i), y: 0, width: view.frame.width, height: view.frame.height)
slideScrollView.addSubview(slides[i])
}
}
func scrollViewDidEndDecelerating(_ scrollView : UIScrollView){
let pageIndex = round(scrollView.contentOffset.x / view.frame.width)
pagerControl.currentPage = Int(pageIndex)
}
}
It's not working because you are passing UIColors instead of CGColors in the .colors Array of the CAGradientLayer.
It should be like this:
func addGradient(){
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [UIColor.black.cgColor, UIColor.orange.cgColor]
let backgroundIndex:UInt32 = 0;
self.layer.insertSublayer(gradient, at: backgroundIndex)
}
I extended UIView to add a addGradientWithColor() method to get the gradient background:
extension UIView {
func addGradientWithColor() {
let gradient = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = [gradientEndColor.CGColor, gradientStartColor.CGColor]
gradient.startPoint = CGPointMake(1,0)
gradient.endPoint = CGPointMake(0.2,1)
self.layer.insertSublayer(gradient, atIndex: 0)
} }
My issue is when I run landscape mode, the UIView is not stretched
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.addGradientWithColor() }
I tried to calling viewDidLayoutSubviews() but its not working properly
Here is the screen shot:
after removing viewDidLayoutSubviews()
You can subclass the UIView and override drawRect method where you add your gradient.
Updated to Swift 4
class GradientView: UIView {
private let gradient : CAGradientLayer = CAGradientLayer()
private let gradientStartColor: UIColor
private let gradientEndColor: UIColor
init(gradientStartColor: UIColor, gradientEndColor: UIColor) {
self.gradientStartColor = gradientStartColor
self.gradientEndColor = gradientEndColor
super.init(frame: .zero)
}
required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
gradient.frame = self.bounds
}
override public func draw(_ rect: CGRect) {
gradient.frame = self.bounds
gradient.colors = [gradientEndColor.cgColor, gradientStartColor.cgColor]
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.endPoint = CGPoint(x: 0.2, y: 1)
if gradient.superlayer == nil {
layer.insertSublayer(gradient, at: 0)
}
}
}
After you create your UIView you just need to add your constraints to that view.
you could save a reference to the layer, and adjust it's frame in the views layoutSublayersOfLayer method. this could be outsourced in an UIView subclass:
class GradientView: UIView {
private let gradient : CAGradientLayer = CAGradientLayer()
/**
displays a gradient on the view
*/
func addGradient() {
self.gradient.frame = self.bounds
self.gradient.colors = [gradientEndColor.CGColor, gradientStartColor.CGColor]
gradient.startPoint = CGPointMake(1,0)
gradient.endPoint = CGPointMake(0.2,1)
self.layer.insertSublayer(self.gradient, atIndex: 0)
}
/**
resizes the gradient with the view size
*/
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
self.gradient.frame = self.bounds
}
}
Layers do not autoresize them self. To fix this issue you should change layer frame. This is one way how it is possible to implement:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
//Update you're layer based on the new frame
self.view.addGradientWithColor()
}
Inspired from solutions here, I've created the following class.
Usage examples:
// Simple usage. From clear to black.
let gradientView1 = GradientView(colors: [.clear, .black])
// Tweak locations. Here the gradient from red to green will take 30% of the view.
let gradientView2 = GradientView(colors: [.red, .green, .blue], locations: [0, 0.3, 1])
// Create your own gradient.
let gradient = CAGradientLayer()
gradient.colors = [UIColor.red.cgColor, UIColor.blue.cgColor]
let gradientView3 = GradientView(gradient: gradient)
The class:
import UIKit
/**
* View with a gradient layer.
*/
class GradientView: UIView {
let gradient : CAGradientLayer
init(gradient: CAGradientLayer) {
self.gradient = gradient
super.init(frame: .zero)
self.gradient.frame = self.bounds
self.layer.insertSublayer(self.gradient, at: 0)
}
convenience init(colors: [UIColor], locations:[Float] = [0.0, 1.0]) {
let gradient = CAGradientLayer()
gradient.colors = colors.map { $0.cgColor }
gradient.locations = locations.map { NSNumber(value: $0) }
self.init(gradient: gradient)
}
override func layoutSublayers(of layer: CALayer) {
super.layoutSublayers(of: layer)
self.gradient.frame = self.bounds
}
required init?(coder: NSCoder) { fatalError("no init(coder:)") }
}