Alpha component on UIBezierPath Fill - ios

I have a view defined like this :
class TriangleView: UIView {
override func drawRect(rect: CGRect) {
// Get Height and Width
let layerHeight = self.layer.frame.height
let layerWidth = self.layer.frame.width
// Create Path
let bezierPath = UIBezierPath()
bezierPath.moveToPoint(CGPointMake(0, 0))
bezierPath.addLineToPoint(CGPointMake(0, layerHeight))
bezierPath.addLineToPoint(CGPointMake(layerWidth, layerHeight + 4))
bezierPath.addLineToPoint(CGPointMake(0, 0))
bezierPath.closePath()
// Apply Color
UIColor(red: (69/255.0), green: (209/255.0), blue: (153/255.0), alpha: 1.0).setFill()
bezierPath.fill()
// Mask to Path
let shapeLayer = CAShapeLayer()
shapeLayer.borderColor = UIColor.clearColor().CGColor
shapeLayer.path = bezierPath.CGPath
self.layer.mask = shapeLayer
}
}
I'm trying to make it semi-transparent, however using UIColor(red: (69/255.0), green: (209/255.0), blue: (153/255.0), alpha: 0.5).setFill() as well as using bezierPath.fillWithBlendMode( .Normal, alpha: 0.5) produces the same result of a darker than normal color with no transparency, the more I decrease the alpha in both cases, the darker the color becomes. I'm not sure what I'm doing wrong.

Set the views backgroundColor to UIColor.clearColor().

I think you are assigning the TriangleView for the main view which present on the ViewController. Instead of that drag another view from the Object Library and assign it as TriangleView

Related

Draw gradient inside UIView draw() with swift

I want to put a gradient background in all my views.
The way that I thought to do this without the need to create a IBOutlet of a view in all view controllers is to create a new class from UIView, and inside the draw() I put the code that makes a CAGradientLayer in the view.
So, in the interface builder I just need to select the background view and set its class as my custom class. It works so far.
My question is if I can do that without problems. Somebody knows is it ok?
Because the model file that inherit from UIView come with the comment:
//Only override draw() if you perform custom drawing.
And I don't know if create a layer counts. Or if draw() is only to low level drawing or something like that. Do not know nothing about the best usage of the draw() func.
This is the code:
override func draw(_ rect: CGRect) {
let layer = CAGradientLayer()
layer.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: self.frame.height)
let color1 = UIColor(red: 4/255, green: 39/255, blue: 105/255, alpha: 1)
let color2 = UIColor(red: 1/255, green: 91/255, blue: 168/255, alpha: 1)
layer.colors = [color1.cgColor, color2.cgColor]
self.layer.addSublayer(layer)
}
You can use #IBDesignable and #IBInspectable to configure the startColor and endColor properties from the Storyboard. Use CGGradient which specifies colors and locations instead of CAGradientLayer if you want to draw in draw(_ rect:) method. The Simple Gradient class and draw(_ rect: CGRect) function would look like this
import UIKit
#IBDesignable
class GradientView: UIView {
#IBInspectable var startColor: UIColor = UIColor(red: 4/255, green: 39/255, blue: 105/255, alpha: 1)
#IBInspectable var endColor: UIColor = UIColor(red: 1/255, green: 91/255, blue: 168/255, alpha: 1)
override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()!
let colors = [startColor.cgColor, endColor.cgColor]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let colorLocations: [CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: colorSpace,
colors: colors as CFArray,
locations: colorLocations)!
let startPoint = CGPoint.zero
let endPoint = CGPoint(x: 0, y: bounds.height)
context.drawLinearGradient(gradient,
start: startPoint,
end: endPoint,
options: [CGGradientDrawingOptions(rawValue: 0)])
}
}
you can read more about it here and tutorial
You shouldn't be adding a CAGradientLayer inside the draw function.
If you want to add a gradient to a view then the easiest way is to do it with a layer (like you are doing) but not like this...
You should add it to your view as part of the init method and then update its frame in the layout subviews method.
Something like this...
class MyView: UIView {
let gradientLayer: CAGradientLayer = {
let l = CAGradientLayer()
let color1 = UIColor(red: 4/255, green: 39/255, blue: 105/255, alpha: 1)
let color2 = UIColor(red: 1/255, green: 91/255, blue: 168/255, alpha: 1)
l.colors = [color1.cgColor, color2.cgColor]
return l
}()
override init(frame: CGRect) {
super.init(frame: frame)
layer.addSublayer(gradientLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}
}
I would create UIView extension and apply it when needed.
extension UIView {
func withGradienBackground(color1: UIColor, color2: UIColor, color3: UIColor, color4: UIColor) {
let layerGradient = CAGradientLayer()
layerGradient.colors = [color1.cgColor, color2.cgColor, color3.cgColor, color4.cgColor]
layerGradient.frame = bounds
layerGradient.startPoint = CGPoint(x: 1.5, y: 1.5)
layerGradient.endPoint = CGPoint(x: 0.0, y: 0.0)
layerGradient.locations = [0.0, 0.3, 0.4, 1.0]
layer.insertSublayer(layerGradient, at: 0)
}
}
Here you can change func declaration to specify startPoint, endPointand locations params as variables.
After that just call this method like
gradienView.withGradienBackground(color1: UIColor.green, color2: UIColor.red, color3: UIColor.blue, color4: UIColor.white)
P.S.
Please note that you can have as much colors as you want in colors array

How to continually change gradient color background?

I currently have a single gradient color background (see below), but I want to gradually change the colors to shades that I will pre-define in an array and have it loop through. How can I achieve this?
var colorTop = UIColor(red: 255.0/255.0, green: 149.0/255.0, blue: 0.0/255.0, alpha: 1.0).cgColor
var colorBottom = UIColor(red: 255.0/255.0, green: 94.0/255.0, blue: 58.0/255.0, alpha: 1.0).cgColor
func configureGradientBackground(colors:CGColor...){
let gradient: CAGradientLayer = CAGradientLayer()
let maxWidth = max(self.view.bounds.size.height,self.view.bounds.size.width)
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 1)
let squareFrame = CGRect(origin: self.view.bounds.origin, size: CGSize(width: maxWidth, height: maxWidth * 0.935))
gradient.frame = squareFrame
gradient.colors = colors
view.layer.insertSublayer(gradient, at: 0)
}
override func viewDidLoad() {
super.viewDidLoad()
configureGradientBackground(colors: colorTop, colorBottom)
}
Really quite straightforward; I can't imagine what difficulty you are having. CAGradientLayer's colors property is itself animatable, so you simply animate the change of colors and, when the animation ends, proceed to the next animation of the change of colors.
In this example I alternate between just two sets of colors, yours and blue-and-green. So my state is a simple Bool. But obviously the example could be extended to any number of states, that is, any number of pairs of colors.
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
var state = false
var grad : CAGradientLayer?
override func viewDidLoad() {
// configure the gradient layer, start the animation
}
func animate() {
let arr = self.state ? [colorTop,colorBottom] : [UIColor.green.cgColor,UIColor.blue.cgColor]
self.state = !self.state
let anim = CABasicAnimation(keyPath: "colors")
anim.duration = 10 // or whatever
anim.fromValue = self.grad!.colors
anim.toValue = arr
anim.delegate = self
self.grad?.add(anim, forKey: nil)
DispatchQueue.main.async {
CATransaction.setDisableActions(true)
self.grad?.colors = arr
}
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
DispatchQueue.main.async {
self.animate()
}
}
You could have a variable in your viewDidLoad that is an int that keeps track of the index of the array that contains all your colors. For example, 0 is your first color, 1 is your second and so on. Then in your infinite while loop (also in the viewDidLoad), you can write:
if tracker == 0 {
// apply first color
} else if tracker == 1{
// apply second color
} ...
Then you can increment the tracker by one at the end and then check your if your tracker is past the size of your array (for example, you only have 11 colors).
tracker += 1
if tracker > 10 {
tracker = 0
}

How can i achieve this type of gradient in each cell?

How can i achieve the gradient below each cell ?
You can try with following code:
class func addGradientInView(_ view : UIImageView , Color1 : UIColor , color2 : UIColor , cornerRadius : CGFloat, width : CGFloat) -> CAGradientLayer
{
view.layer.cornerRadius = cornerRadius
view.layer.masksToBounds = true
view.layer.layoutSublayers()
let labelGradient: CAGradientLayer = CAGradientLayer()
labelGradient.masksToBounds = true
labelGradient.frame = view.bounds
labelGradient.frame.size.width = width
labelGradient.frame.size.height = width
labelGradient.colors = [(Color1.cgColor as AnyObject), (color2.cgColor as AnyObject)]
view.layer.insertSublayer(labelGradient, at: 0)
return labelGradient
}
To call the above method:
addGradientInView(icon_Veges, Color1: darkGray_MaxAlpha,
color2:darkGray_MinAlpha,
cornerRadius: 0.0,
width:width)
Suppose: let darkGray_MaxAlpha = UIColor(red: 255/255.0, green: 255/255.0, blue: 255/255.0, alpha: 0.1)
let darkGray_MinAlpha = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0.6)
Note: Put view, color 1, color 2, width according to your need.
Create a UIView and subview the items you wanted like Name, Place etc.
Then initialise an object of CAGradientLayer and set colors, locations and frame. Then sublayer the object to UIView.
Following is the code snippet I suggest.
let gradient = CAGradientLayer()
gradient.colors = [UIColor.blackColor().colorWithAlphaComponent(0).CGColor,UIColor.blackColor().colorWithAlphaComponent(0.3).CGColor]
gradient.locations = [0.1,1.0]
gradient.frame = gradientView.bounds
gradientView.layer.addSublayer(gradient)

How to change background color of UITextField to custom gradient color in iOS using Swift

I have done gradient coloring before for changing color of my parent view now I am using same method to change the background color of UITextField but its not working.
This is the method I used for my view:
func setGradientBackgroundOfView()
{
// assigning gradient color to background View
let endingColorOFGradient = UIColor(colorLiteralRed: 133/255, green: 210/255, blue: 230/255, alpha: 1.0).CGColor
let startingColorOfGradient = UIColor(colorLiteralRed: 50/255, green: 189/255, blue: 170/255, alpha: 1.0).CGColor
gradient.startPoint = CGPoint(x: 0.5, y: 0.0)
gradient.endPoint = CGPoint(x: 0.5, y: 1.0)
gradient.colors = [startingColorOfGradient , endingColorOFGradient]
self.view.layer.insertSublayer(gradient, atIndex: 0)
}
And this method for setting layer:
override func viewWillLayoutSubviews() {
gradient.frame = self.view.bounds
}
I am calling my setGradientBackgroundOfView() in viewDidLoad() and it's working perfect but if I change
self.view.layer.insertSublayer(gradient, atIndex: 0)
with
self.textFieldUserName.layer.insertSublayer(gradient, atIndex: 0)
it does not work. What am I missing here?

CALayer not resizing with Autolayout

I have created a progress bar to be used in a tableView by creating a gradient layer. It works perfectly.
iPhone5:
In order to use the app on multiple devices, I have created the UIView in Storyboard, tagged it and added constraints.
However, when I use the app on an iPhone 6 the CALayer don't resize.
iPhone6:
I find this extremely stupid, but never mind. I have looked around and tried to understand how to solve this for months, but I have come up short. Does ANYONE know how to make CALayers resize with the UIView? Any help would be very much appreciated ! Thank you.
progressBar = cell.contentView.viewWithTag(3) as UIView!
progressBar.layer.cornerRadius = 4
progressBar.layer.masksToBounds = true
// create gradient layer
let gradient : CAGradientLayer = CAGradientLayer()
// create color array
let arrayColors: [AnyObject] = [
UIColor (red: 255/255, green: 138/255, blue: 1/255, alpha: 1).CGColor,
UIColor (red: 110/255, green: 110/255, blue: 118/255, alpha: 1).CGColor]
// set gradient frame bounds to match progressBar bounds
gradient.frame = progressBar.bounds
// set gradient's color array
gradient.colors = arrayColors
//Set progress(progressBar)
var percentageCompleted = 0.6
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [percentageCompleted, percentageCompleted]
gradient.endPoint = CGPoint(x: 1, y: 0.5)
// replace base layer with gradient layer
progressBar.layer.insertSublayer(gradient, atIndex: 0)
The default layer of a UIView does resize with its view, but sublayers don't (as you found out). One way to make this work is to create a custom view class, move the code you have in your question to it, and override layoutSublayersOfLayer where you can set the gradient layer to be the same size as the view. Because this code is now in a custom class, I also created a property percentageCompleted (instead of a local variable), and added a willSet clause so the bar's appearance is updated any time you change the percentageCompleted property.
class RDProgressView: UIView {
private let gradient : CAGradientLayer = CAGradientLayer()
var percentageCompleted: Double = 0.0 {
willSet{
gradient.locations = [newValue, newValue]
}
}
override func awakeFromNib() {
self.layer.cornerRadius = 4
self.layer.masksToBounds = true
// create color array
let arrayColors: [AnyObject] = [
UIColor (red: 255/255, green: 138/255, blue: 1/255, alpha: 1).CGColor,
UIColor (red: 110/255, green: 110/255, blue: 118/255, alpha: 1).CGColor]
// set gradient's color array
gradient.colors = arrayColors
//Set progress(progressBar)
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.locations = [percentageCompleted, percentageCompleted]
gradient.endPoint = CGPoint(x: 1, y: 0.5)
self.layer.insertSublayer(gradient, atIndex: 0)
}
override func layoutSublayersOfLayer(layer: CALayer!) {
super.layoutSublayersOfLayer(layer)
gradient.frame = self.bounds
}
}
In IB, you would change the class of your view to RDProgressView (in my example), and in cellForRowAtIndexPath, you would only need to get a reference to the view, and set its percentageCompleted property.
progressBar = cell.contentView.viewWithTag(3) as RDProgressView!
progressBar.percentageCompleted = 0.2
As an alternative to the accepted answer, you could also change the views layer class to be CAGradientLayer. The views layer will always be resized according to layout changes. You can achieve that by subclassing UIView
class GradientView: UIView {
override class func layerClass() -> AnyClass {
return CAGradientLayer.self
}
}
then set the colors
if let gradientLayer = gradientView.layer as? CAGradientLayer {
gradientLayer.colors = arrayColors
}
It's less code than adding and maintaining a sublayer, but might not suit all use cases.

Resources