Create Gradient in View With locations from zeplin - ios

I am working on a project where i need to apply gradients to the view. I am having sketch file and have colors which i am going to use for gradient with the locations but i am unable to get the exact view.
Can anyone please help how to get that?
I have created a function to apply gradient:-
func applyGradient(colours: [UIColor]) -> Void {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.frame = self.bounds
gradient.colors = colours.map { $0.cgColor }
self.layer.insertSublayer(gradient, at: 0)
}
func applyGradientToView(){
let firstColor = UIColor(red: 26/255, green: 169/255, blue: 186/255, alpha: 1.0)
let secondColor = UIColor(red: 26/255, green: 97/255, blue: 157/255, alpha: 1.0)
let thirdColor = UIColor(red: 27/255, green: 65/255, blue: 144/255, alpha: 1.0)
self.applyGradient(colours: [firstColor, secondColor, thirdColor])
}
Here array UIcolor is a combination of colors to be used, I used all three but still, I didn't get the same as in the sketch
What I created :-
Gradient colors in sketch file:-
The view in sketch file is like this:-

Couple issues...
1) Your "Gradient colors in sketch file:" image does not match your Sketch file output image. However you applied the gradient in sketch, it is not a full top-to-bottom (or, in this case, bottom-to-top) gradient fill.
2) When working with colors in different applications and/or devices, you have to be aware of "color spaces". Take a quick search on google for sketch colors don't match and you'll find lots of material explaining it, along with tips on using sketch for iOS targets.
So, an easy way to get close to your desired output is to use OS X Digital Color Meter - which should be installed by default on your Mac (if it's not, it's easy to find). That tool allows you to hover over a point on your image and get the RGB values based on different color spaces. SRGB should be a match.
Also, there is a better way to code your custom view for reuse. Take a look at this approach:
class MyGradientView: UIView {
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
func commonInit() -> Void {
let gradientLayer = layer as! CAGradientLayer
let firstColor = UIColor(red: 25/255, green: 138/255, blue: 173/255, alpha: 1.0)
let secondColor = UIColor(red: 27/255, green: 163/255, blue: 184/255, alpha: 1.0)
let colours = [firstColor, secondColor]
gradientLayer.colors = colours.map { $0.cgColor }
gradientLayer.shadowColor = UIColor.black.cgColor
gradientLayer.shadowOffset = CGSize(width: 0.0, height: 1.0)
gradientLayer.shadowRadius = 2.0
gradientLayer.shadowOpacity = 0.5
gradientLayer.masksToBounds = false
}
}
Gradient layers in iOS by default go from Top-To-Bottom, so you only need your top and bottom colors defined. This approach also includes your shadow (as shown in your sketch output image). And it will maintain the gradient and shadow when using the custom view with auto-layout:
let v = MyGradientView()
view.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
v.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0).isActive = true
v.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -100.0).isActive = true
v.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0).isActive = true
v.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40.0).isActive = true
Result:

According to the documentation of CAGradientLayer (https://developer.apple.com/documentation/quartzcore/cagradientlayer) to specify a location of colors you can use locations property(in your case [0, 0.68, 1]):
var locations: [NSNumber]? { get set }
but as #Alladinian wrote in the comment in your example, it looks like your gradient was drawn from bottom to top and start before view and end far after view so you see only a part

Related

Is there a way to change all of my IOS App page's Bg Color by using User Defaults?

I am following this tutorial provided on Youtube for: How to Save Data with UserDefaults - Swift
https://www.youtube.com/watch?v=sUhq1vIrRbo
And I have this code that works for one page only and would like to know how to do the same exact thing (changing background color) but for my entire app pages based on the user's choice.
I have tried keeping the checkForStylePreference() in the viewDidLoad()of another page but it did not recognize it. I copy pasted the whole checkForStylePreference() but still other pieces of code were missing. Is the only way to do it is by copy pasting all of the methods of the viewController in all App pages? Or there is a much simpler way as a believe to reduce amount of code? Currently I can change BgColor from white to grey perfectly enter image description here but I don't know how to apply it for all.
This is the code of my NameViewController.swift (the one I've created for the page in the screenshot). Please note that I have 2 more swift files which are SAButton.swift and ConstantStyles.swift (for the colors)
class NameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
nameLbl.text = myString
checkForStylePreference()
}
#IBAction func didChangeStyleSeg(_ sender: UISegmentedControl) {
isDarkMode = sender.selectedSegmentIndex == 1
saveStylePreference()
updateStyle()
}
var myString = String()
#IBOutlet weak var styleSegment: UISegmentedControl!
#IBOutlet weak var nameLbl: UILabel!
var isDarkMode = false
let defaults = UserDefaults.standard
struct Keys {
static let preferDarkMode = "preferDarkMode"
}
func updateStyle(){
UIView.animate(withDuration: 0.4){
// self.view.backgroundColor = self.isDarkMode ? Colors.darkGrey : .white
// UIColor(hue: 287/360, saturation: 15/100, brightness: 85/100, alpha: 1.0)
self.view.backgroundColor = self.isDarkMode ? Colors.lightGrey : .white
//recent correct one
// self.view.backgroundColor = self.isDarkMode ? Colors.darkGrey : .white
//self.view.UIBackgroundFetchResult = self.isDarkMode? UIColor.grey : .white
}
}
func saveStylePreference(){
defaults.set(isDarkMode, forKey: Keys.preferDarkMode)
}
func checkForStylePreference(){
let preferDarkMode = defaults.bool(forKey: Keys.preferDarkMode)
if preferDarkMode{
isDarkMode = true
updateStyle()
styleSegment.selectedSegmentIndex = 1
}
}
// Do any additional setup after loading the view.
}
Code of the SAButton.swift
class SAButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
setupButton()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupButton()
}
private func setupButton() {
setTitleColor(.white, for: .normal)
backgroundColor = Colors.lightBlue
titleLabel?.font = .boldSystemFont(ofSize: 20)
layer.cornerRadius = frame.size.height / 2
}
}
Code of the ConstantStyles.swift
import UIKit
struct Colors {
static let darkGrey = UIColor(red: 40/255, green: 40/255, blue: 40/255, alpha: 1)
// static let purple = UIColor(red: 212/255, green: 186/255, blue: 86/255, alpha: 1)
static let lightBlue = UIColor(red: 89/255, green: 205/255, blue: 242/255, alpha: 1)
static let darkPurple = UIColor(red: 242/255, green: 232/255, blue: 255/255, alpha: 1.0)
// UIColor(hue: 287/360, saturation: 15/100, brightness: 85/100, alpha: 1.0)
static let lightPurple = UIColor(red: 240/255, green: 229/255, blue: 255/255, alpha: 1.0)
static let lightGrey = UIColor(red: 237/255, green: 237/255, blue: 237/255, alpha: 1.0)
//UIColor(red: 249/255, green: 244/255, blue: 255/255, alpha: 1.0)
}
I believe it could be simple but I am new to Swift, I would like to know what part of code to keep exactly and where. Much appreciated.
Ps: Original project Source Code is provided below the Youtube Video.
You can create a main class and inherit from it
class GeneralVC : UIViewController {
override func viewDidLoad() {
self.view.backgroundColor = .red // read color from userdefaults and set it here
}
}
class ViewController: GeneralVC {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Same applies to any UIKit component that you need to affect globally
Another interesting way to do it is to use Appearance:
Perhaps you can use UIViewControllerWrapperView as a parent.
UIView.appearance(whenContainedInInstancesOf: [UIViewControllerWrapperView]) // UIViewControllerWrapperView might be private. In that case it might take some wizardry to get it to work
Another way to do it is to set it when the UITabBarController or UINavigationController presents a new UIViewController. You can do this by subclassing them.
The reason why I don't like subclassing is that you force a subclass for just one simple thing. If you only do it in a few navigation based ones it's much easier and also easier to override with extensions instead of everything through subclassing.

How to set background of a view (UITableview) with dynamic cells?

I am trying to set a background Colour to the view occupied by the UITableViewCell. I have dragged an outlet and have a gradient layer which i return to it
extension CAGradientLayer{
func viewGradient(topColour:UIColor,bottomColour : UIColor) -> CAGradientLayer{
let gradientColor: [CGColor] = [topColour.cgColor, bottomColour.cgColor]
let gradientLocations: [Float] = [0.0/1.0]
let gradientLayer: CAGradientLayer = CAGradientLayer()
gradientLayer.colors = gradientColor
gradientLayer.locations = gradientLocations as [NSNumber]?
return gradientLayer
}
}
And i access this method in my viewDidLoad method of a view controller
let bottomColour = UIColor(red: (0.018), green: (0.38), blue: (0.64), alpha: 1.0)
let topColour = UIColor(red: (0.13), green: (0.63), blue: (0.67), alpha: 0.7)
let background = CAGradientLayer().viewGradient(topColour:topColour,bottomColour : bottomColour)
background.frame = self.view.bounds
self.view.layer.insertSublayer(background, at: 0)
myMenu.layer.insertSublayer(background, at: 0)
I have dynamic cells in my table view and as the number of cells increases the view slides down and i Have to scroll to see my cells.I have white spaces. Is there any way i can set the entire scrolling region to have my background gradient colour or am i missing something ?
Here is the screenshot of the images showing my exact problem
As commented above, UITableView has a backgroundView property.
In your viewDidLoad method, keep your background constant declared as is, then below add:
self.tableView.backgroundView = background
Hope that helps.

Setting custom RGB color in drawing class in Swift

In the below simplified drawing function, I am able to set the alpha of a drawing stroke.
I have a custom color with RGB values, for example Red = 0.25, Green = 0.5, Blue = 0.75.
How do I set a custom RGB stroke color in the drawing function similar to setting the alpha?
Is it also possible to set a stroke color together with setting the stroke alpha in a single statement?
override func drawRect(rect: CGRect) {
strokeColor.colorWithAlphaComponent(0.5).setStroke()
path?.stroke()
}
If you want to set the color try this:
var Color = UIColor(red: 200.0/255.0, green: 16.0/255.0, blue: 46.0/255.0, alpha: 1.0)
also a very useful tool/ website for converting the colors into useful Swift code:
https://www.ralfebert.de/snippets/ios/swift-uicolor-picker/
Edit:
self.button.setTitleColor(UIColor(red:0.96, green:0.28, blue:0.28, alpha:1.0), forState: .Normal)
or if you want a cleaner solution:
var myColor = UIColor(red:0.96, green:0.28, blue:0.28, alpha:1.0)
self.button.setTitleColor(myColor, forState: .Normal)

I'd like to create a CAGradientLayer Subclass/Utility class

I've been programming in one way or another for 30+ years, but I'm new to Swift and iOS programming and working on my first app. I've worked out how to create a CAGradientLayer to show a gradient on screen, but I'd like to create a utility class/extension/subclass with some predefined gradients in it.
I am assuming I can do this by creating subclasses of CAGradientLayer with the properties pre-set at initialisation time, but I'm not sure of the correct syntax to do this. I guess I am missing some basic Swift requirements?
I've tried the following:
let greenBlueGradient = GreenBlueGradient.greenBlueGradient()
class GreenBlueGradient: CAGradientLayer {
override init() {
super.init()
}
func greenBlueGradient() -> GreenBlueGradient {
self.colors = [ UIColor.init( red: 0.05, green: 0.75, blue: 0.91, alpha: 1.0 ).CGColor, UIColor.init( red: 0.56, green: 0.93, blue: 0.56, alpha: 1.0 ).CGColor ]
return self
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
But I'm getting the error "Missing argument for parameter #1 in call" on the let line. What am I missing? Surely there is a more efficient way of doing this? I sort of have in my head that I will be able to create some sort of utility class/extension that will return a CAGradientLayer with the colors already set ready for me to set the frame and insert it into my view hierarchy.
You shoud add an extension of CAGradientLayer in other file for exemple :
in file "UIExtensionsCAGradientLayer.swift"
extension CAGradientLayer {
func setupGreenBlurGradient(){
self.colors = [UIColor.init( red: 0.05,
green: 0.75,
blue: 0.91,
alpha: 1.0).CGColor,
UIColor.init( red: 0.56,
green: 0.93,
blue: 0.56,
alpha: 1.0 ).CGColor ]
}
}
Then in the file where you need the green blur gradient you do :
let greenGradientBlur = CAGradientLayer(layer : layer)
greenGradientBlur.setupGreenBlueGradient()
The error message here is a bit misleading. (File a bug about that. Or fix it yourself, if you're so inclined, now that Swift is open source.)
Your attempt to call GreenBlueGradient.greenBlueGradient() fails because you're calling an instance method as if it were a class method. That call would succeed if you'd declared greenBlueGradient() as a class func. However, that function's implementation relies on self.
What you probably want instead is to add your customization to init:
override init() {
super.init()
self.colors = [
UIColor(red: 0.05, green: 0.75, blue: 0.91, alpha: 1.0).CGColor,
UIColor(red: 0.56, green: 0.93, blue: 0.56, alpha: 1.0).CGColor,
]
}
That way, a client can get your custom gradient just by constructing an instance:
let greenBlueGradient = GreenBlueGradient()
Pretty colors, by the way.

Adding a Gradient Background to UITableViewCell Swift

I found this SO question here:
Swift: gradient on a cell of a tableview
I am using a Custom UITableViewCell which has the following code:
public class MyCustomUITableViewCell: UITableViewCell {
override public func layoutSubviews() {
super.layoutSubviews()
//setting backgroundColor works
//self.backgroundColor = UIColor.brownColor()
self.backgroundColor = UIColor.clearColor()
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
let gradient = CAGradientLayer()
gradient.colors = [ colorTop, colorBottom]
gradient.locations = [ 0.0, 1.0]
self.backgroundView = UIView()
//setting view backgroundColor does not work
//self.backgroundView?.backgroundColor = UIColor.redColor()
self.backgroundView!.layer.insertSublayer(gradient, atIndex: 0)
}
}
The UITableViewCells displayed are clear because I set the self.backgroundColor to UIColor.clearColor().
The gradient does not show, and if, instead of adding a gradient to the UITableViewCell.backgroundView,I just set the UITablveViewCell.backgroundView.backgroundColor to UIColor.redColor(), that does not work either.
When I create the UITableViewCell.backgroundView at this line of code:
self.backgroundView = UIView()
I am assuming the backgroundView automatically fills the bounds of the UITableViewCells displayed in the UITableView, in other words, backgroundView is not a 0x0 width x height correct?
This might help you and others: it's the (tiny) UIView subclass I use to draw gradients without having to get into the mess of inserting CALayers. This way UIView handles resizing using things like Auto Layout, which is much easier to work with.
Put this code into a file in your project, then use it as a normal view. It can go straight into your cell's background view if you want. You should be able to customise the colours in code or in IB if you use it that way.
It's trivial code, but please consider it CC-0 licensed – i.e. public domain where possible, "just use it however you want" everywhere else.
#IBDesignable class GradientView: UIView {
#IBInspectable var firstColor: UIColor = UIColor.whiteColor()
#IBInspectable var secondColor: UIColor = UIColor.blackColor()
override class func layerClass() -> AnyClass {
return CAGradientLayer.self
}
override func layoutSubviews() {
(layer as! CAGradientLayer).colors = [firstColor.CGColor, secondColor.CGColor]
}
}
If you're looking for more advanced functionality, try something like SwiftyGradient – it does much the same thing (i.e. pushing the work into a UIView to make things easier), but has more functionality.
As noted in the documentation for UITableViewCell:
If you want to change the background color of a cell, do so in the
tableView:willDisplayCell:forRowAtIndexPath: method of your table view
delegate.
I normally just add another view to my cells that I use as background view, that works in all cases.

Resources