How to add one more CAGradientLayer to UIView? - ios

I have a PopupView with a scrollable label (like Marquee type). Its scrolled inside a labelView, which is just a UIView.
I decided to add an effect of transparent gradient to the left and right sides of labelView using the following code:
let gradientLayer = CAGradientLayer()
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 0)
gradientLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor, UIColor.white.cgColor]
gradientLayer.locations = [0, 0.2, 1]
gradientLayer.frame = labelView.bounds
labelView.layer.mask = gradientLayer
But the effect is visible only on the left side of the labelView. Right side is unchanged:
How can I apply the same gradient to the labelView right side?

You should change your start/end points and your color locations
In a linear Gradient locations are relative points where [colors] are presented.
0 0.2 0.8 1
|--|-----|--|
so from 0 to 0.2 (20% of total length) we will have a gradient of clear to white, then from 0.2 (20%) to 0.8 (80%) a gradient from white to white (solid white) and from 0.8 (80%) to 1 (100%) a gradient from white to clear color.
From apple https://developer.apple.com/documentation/quartzcore/cagradientlayer/1462410-locations
Discussion
The gradient stops are specified as values between 0 and 1. The values must be monotonically increasing. If nil, the stops are spread uniformly across the range. Defaults to nil.
When rendered, the colors are mapped to the output color space before being interpolated.
let gradientLayer = CAGradientLayer()
gradientLayer.startPoint = CGPoint(x: 0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 1, y: 0.5)
gradientLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor, UIColor.white.cgColor, UIColor.clear.cgColor]
gradientLayer.locations = [0, 0.2, 0.8, 1]
gradientLayer.frame = labelView.bounds
labelView.layer.mask = gradientLayer
After Updated answer

Related

Create gradient with 4 colors and small intersections?

There are a lot of questions here about how to create multicolor gradients, but what I am struggling is to create gradient where gradient areas are small. For example i want to have 4 colors on view, and I want 3 intersections between colors to have small gradients.
gradientLayer = CAGradientLayer()
gradientLayer.frame = gradientFrame
let locations = [Float](arrayLiteral: 0.3, 0.35, 0.8, 0.85)
gradientLayer.locations = locations.map { NSNumber(value: $0) }
gradientLayer.colors = [UIColor.red.cgColor,
UIColor.orange.cgColor,
UIColor.green.cgColor,
UIColor.gray.cgColor]
layer.insertSublayer(gradientLayer, at: 0)
I get nice gradient between red and orange, and green and gray. But gradient between orange and green is much larger and doesn't look right.
I have not been able to fix this. Any solutions?
Use green twice in the sequence of colors. Red orange green green gray. Now you can have a big solid green in the middle and thin transitions on either side of it. Example:
let locations = [Float](arrayLiteral: 0.3, 0.35, 0.4, 0.8, 0.85)
gradientLayer.locations = locations.map { NSNumber(value: $0) }
gradientLayer.colors = [UIColor.red.cgColor,
UIColor.orange.cgColor,
UIColor.green.cgColor,
UIColor.green.cgColor,
UIColor.gray.cgColor]
That's the general principle; just tweak it however you like.

How to create percentage(%) wise three colour gradient in Swift?

I want to create a gradient view with three colours in swift.
linear-gradient(to right, #232c63 10%,#1d6cc1 59%,#33bdd6 100%);
How to define colours with %.
First Colour with 10 %
Second Colour with 59 %
Third Colour with 100 %
Example:
If you want to use hex colors, use extension like these: https://stackoverflow.com/a/24263296/4711785
After that you can use colorWithAlphaComponent instance method like above which is mentioned in official documentation
UIColor.black.withAlphaComponent(0.7)
Try using the locations array of CAGradientLayer to give the startPoint for each color in the gradient, i.e.
let gradient = CAGradientLayer()
gradient.colors = [UIColor.red.cgColor, UIColor.blue.cgColor, UIColor.green.cgColor]
gradient.startPoint = .zero
gradient.endPoint = CGPoint(x: 1, y: 0)
gradient.locations = [0, 0.1, 0.59]
gradient.frame = view.bounds
view.layer.addSublayer(gradient)
In the above code, locations are calculated using the required percentage, i.e. 10%, 59%, 100%.
Also add the colors as per your requirement.
var locations: [NSNumber]? { get set }
An optional array of NSNumber objects defining the location of each
gradient stop. Animatable.
The gradient stops are specified as values between 0 and 1. The values
must be monotonically increasing. If nil, the stops are spread
uniformly across the range. Defaults to nil.
With this method you can choose the three colours you want:
func gradient(initialColor: UIColor, middleColor: UIColor,
finalColor: UIColor) -> CAGradientLayer {
let gradient = CAGradientLayer()
gradient.frame = self.frame
gradient.colors = [initialColor.withAlphaComponent(1.0), middleColor.withAlphaComponent(0.59), finalColor.withAlphaComponent(1.0)].map{$0.cgColor}
gradient.locations = [0.29, 0.60, 1]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint (x: 1.0, y: 0.5)
return gradient
}
It returns a CAGradientLayer that you can add directly into your view.
When using the "locations" parameter, you can choose where your colours are going to be displayed in terms of percentage. In my method they are: 0% (0 value), 50% (0.5 value) and 100% (1 value).
I recommend using that method inside a UIView extension as a best practice and reusable code in your app.

Create a UIView with leading / trailing faded edges

I have a UIView that I would like to apply a fade / gradient too. I'd like this to appear only on the edges, the effect I am trying to create is
The grey line at the top of the image. Grey in the middle and both edges are faded to white.
I have tried something like this
func render(content: FeedItem) {
print(content.item.externalId)
// rowSeperatorView.backgroundColor = UIColor.usingHex("f2f2f2")
iconContainerView.backgroundColor = UIColor.usingHex("3bac58")
let gradientLayer: CAGradientLayer = CAGradientLayer()
gradientLayer.frame = rowSeperatorView.bounds
gradientLayer.colors = [
UIColor.white.cgColor,
UIColor.usingHex("f2f2f2").cgColor,
UIColor.white.cgColor,
]
gradientLayer.locations = [0,0.5,1]
rowSeperatorView.layer.addSublayer(gradientLayer)
iconContainerView.layer.cornerRadius = 5
iconContainerView.clipsToBounds = true
iconContainerView.layer.maskedCorners = [.layerMaxXMaxYCorner, .layerMaxXMinYCorner]
}
But cannot seem to achieve this result.
You are almost there, try adding a startPoint and endPoint and then play with the locations property.
let gradient = CAGradientLayer()
gradient.frame = rowSeperatorView.bounds
gradient.colors = [UIColor.white.cgColor, UIColor.usingHex("f2f2f2").cgColor, UIColor.white.cgColor]
gradient.locations = [0, 0.4, 0.6]
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x: 1.0, y: 0.0)
rowSeperatorView.layer.addSublayer(gradient)

Swift gradient from black to fully transparent

I am trying to apply a CAGradientLayer using swift, and I would like it to fade from black to transparent.
I thought putting any color with an alpha: 0 would render the same "transparent" color. It is not the case. White with alpha: 0 is still a bit white, red with alpha: 0 is still a bit red...
I don't want any tint, just transparent. The black being less black to the point theres no color and you can fully see the view under it for example.
gradientLayer.colors = [UIColor(hex: 0x000000, alpha: 0.4).cgColor,
UIColor(hex: 0x6D6D6D, alpha: 0.3).cgColor,
UIColor.white.withAlphaComponent(0.0).cgColor]
gradientLayer.locations = [0.0, 0.3, 1.0]
If I apply the layer over a gray picture, it's easy to see that its not transparent but white:
EDIT 1:
gradientLayer.frame = self.gradientView.bounds
gradientLayer.masksToBounds = true
gradientLayer.colors = [UIColor.black.withAlphaComponent(1.0).cgColor, UIColor.black.withAlphaComponent(0.0).cgColor]
gradientLayer.locations = [0.0, 1.0]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0)
self.gradientView.layer.addSublayer(gradientLayer)
Output :
You can try this:
gradientLayer.colors = [UIColor.black.withAlphaComponent(1.0).cgColor,
UIColor.black.withAlphaComponent(0.0).cgColor]
//gradientLayer.locations = [0.0, 1.0]
gradientLayer.frame = myView.bound
gradientLayer.startPoint = CGPoint(x: 1.0, y: 0.0)
gradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
myView.layer.insertSublayer(gradientLayer, at: 0)
It worked in my case.

How to get a circle shape layer with one edge as a gradient and the other one as a full color

I have done this circle gradient layer:
What I would like to have is only one gradient (the one on the left) while the gradient on the bottom would be removed to show a clear separation between red and yellow.
As I will need to make an animation out of it (like a loading view), I thought about having an image in the background and having a circle shape layer with a color (like white) on top and change the stroke of this layer as I need.
Another solution I thought about was having tow circle shape layers, one with the gradient, the other one without.
But both those solutions feels more like a hack and I was wondering if there was a proper one using just
Here is the code I used:
fileprivate func createProgressLayer() {
let startAngle = CGFloat(M_PI_2)
let endAngle = CGFloat(M_PI * 2 + M_PI_2)
let centerPoint = CGPoint(x: frame.width / 2 , y: frame.height / 2)
progressLayer.path = UIBezierPath(arcCenter:centerPoint, radius: frame.width / 2 - 30.0, startAngle: startAngle, endAngle: endAngle, clockwise: true).cgPath
progressLayer.backgroundColor = UIColor.clear.cgColor
progressLayer.fillColor = nil
progressLayer.strokeColor = UIColor.black.cgColor
progressLayer.lineWidth = 4.0
progressLayer.strokeStart = 0.0
progressLayer.strokeEnd = 1.0
let gradientMaskLayer = gradientMask()
gradientMaskLayer.mask = progressLayer
layer.addSublayer(gradientMaskLayer)
}
fileprivate func gradientMask() -> CAGradientLayer {
let gradientLayer = CAGradientLayer()
gradientLayer.frame = bounds
gradientLayer.locations = [0.0, 0.1]
gradientLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 0)
let arrayOfColors: [AnyObject] = [UIColor.red.cgColor, UIColor.yellow.cgColor]
gradientLayer.colors = arrayOfColors
return gradientLayer
}
Shape layers only support a single color.
In order to get the effect that you're after I think you're going to need to use a shape layer as a mask on your gradient like you're doing.
Having a second white shape layer on top of the gradient+mask layer and changing strokeStart and/or strokeEnd also sounds like a reasonable way to do what you're trying to do.
Doing tricks ("hacks" as you call them) with layers is quite common and a reasonable way to do what you're trying to do.

Resources