CAGradientLayer Mask add Opacity in Swift - ios

I'm trying to add opacity to my view. When I add a CAGradientlayer, I roughly get, what I planned, but have a few issues.
I can't change the color. No matter what values I use, I'll always get a white layer
Even though I wanted it to be distributed over the whole view, it seems like it has an offset to the left.
My code is as follows:
let maskLayer = CAGradientLayer()
maskLayer.anchorPoint = CGPoint(x: 0.0, y: 0.0)
maskLayer.startPoint = CGPoint(x: 0.0, y: 0.5)
maskLayer.endPoint = CGPoint(x: 1.0, y: 0.5)
maskLayer.colors = [UIColor(white: 0.0, alpha: 0.0).CGColor, UIColor(white: 1.0, alpha: 1.0).CGColor, UIColor(white: 0.0, alpha: 0.0).CGColor]
maskLayer.locations = [0.0, 0.5, 1.0]
maskLayer.bounds = CGRectMake(0, 0, self.frame.width, self.frame.height)
self.layer.mask = maskLayer
What am I doing wrong?
Thanks

About the white, this is because a layer mask does not contribute to color, only transparency. Essentially, only the alpha component has an effect, and the R,G,B components of the layer mask are ignored.
About the offset, could it be because your view is getting resized, and your layer isn't kept up to date? If so you could see the answer to this other question: CALayers didn't get resized on its UIView's bounds change. Why?

Related

How to implement a black gradient on an image in iOS using swift

I am working on an iOS project where I have white labels on bright images. The problem is for bright images the white labels are not showing. Here is an example:
Label not showing: https://imgur.com/hKtejHn
Label showing: https://imgur.com/Ef5qJAh
I think if I add a black gradient on all the image then the white labels will be visible. Can anyone help me as to how to implement the solution in Swift?
Thank!
If you want to add gradient on your imageView then you can just implement CAGradientLayer on your imageView.layer.
Try to change some values for your own custom look, but the code below is pretty much it.
let gradientLayer = CAGradientLayer()
gradientLayer.frame = imageView.frame
let colors = [
UIColor(red: 0, green: 0, blue: 0, alpha: 1).cgColor,
UIColor(red: 0, green: 0, blue: 0, alpha: 0).cgColor
]
gradientLayer.startPoint = CGPoint(x: 0.1, y: 0.5)
gradientLayer.endPoint = CGPoint(x: 0.9, y: 0.5)
gradientLayer.colors = colors
imageView.layer.addSublayer(gradientLayer)
You can change colors, add colors, change start/end-points. You can find a lot of different CAGradientLayer-guides on youtube or google.
try this:
extension UILabel {
func lblShadow(color: UIColor , radius: CGFloat, opacity: Float){
self.textColor = color
self.layer.masksToBounds = false
self.layer.shadowRadius = radius
self.layer.shadowOpacity = opacity
self.layer.shadowOffset = CGSize(width: 1, height: 1)
self.layer.shouldRasterize = true
self.layer.rasterizationScale = UIScreen.main.scale
}
}
usage:
label.lblShadow(color: UIColor.white, radius: 3, opacity: 0.75)

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 draw gradient with custom equation in CALayer or CGContext in iOS?

I want to draw a linear gradient in different colorspace in iOS device with CoreAnimation or CoreGraphics. However, I found that CAGradientLayer(Linear gradient) only draws linear in device colorspace sense, and also alpha addition is considered in device colorspace. Specifically, I want to simulate another gamma correction. Is there anyway to give custom gamma curve to the gradient?
Moreover, the ultimate objective is to make it animatable. (E.g. define gamma animatable property on CALayer.) Any related idea is okay. I could not find discussions about this.
I don't exact syntax for objective c but I can help u in swift.
Here I created an extension for a view to show gradient effect.
extension UIView {
func layerGradient() {
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [UIColor.UIColorFromRGB(fromHex: 0x26344b).cgColor, UIColor.UIColorFromRGB(fromHex: 0x30bdb7).cgColor]
gradient.locations = [0.0 , 1.0]
gradient.startPoint = CGPoint(x: 0.0, y: 1.0)
gradient.endPoint = CGPoint(x: 1.0, y: 1.0)
gradient.frame = CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.width, height: self.frame.size.height)
self.layer.insertSublayer(gradient, at: 0)
}
}

UIVisualEffectView with CAGradientLayer not working on iOS 10

I'm trying to create an effect whereby a photo is blurry at the top, but not at the bottom, and the blurriness 'fades off' gradually. I achieved this with the code below, which worked fine in iOS9, but does not in iOS10.
I'm aware of a known bug, as described in this question, that prevents a layer having a mask and a blur on the same layer.
The difference between my question and the one linked, is I'm not interested in using a CAShapeLayer as my mask, but rather a CAGradientLayer. I've tried fiddling with adding views/masks/layers in different orders, but am not having much luck.
var visualEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .light))
visualEffectView.frame = CGRect(x: 0.0, y:0.0, width: photo.bounds.width, height: photo.bounds.height)
photo.addSubview(visualEffectView)
let maskStartColour = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
let maskEndColour = UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
let gradient = CAGradientLayer()
gradient.frame = CGRect(x: 0, y: 0, width: visualEffectView.bounds.width, height: visualEffectView.bounds.height)
let colors: [AnyObject] = [maskStartColour.cgColor, maskEndColour.cgColor]
gradient.colors = colors
gradient.startPoint = CGPoint(x: 0.0, y: 0.0)
gradient.endPoint = CGPoint(x: 0.0, y: 1.0)
visualEffectView.layer.mask = gradient

Using drawRect to draw & animate two graphs. A background graph, and a foreground graph

I am using drawRect to draw a pretty simple shape (dark blue in the image below).
I'd like this to animate from the left to the right, so that it grows. The caveat here is I need there to be a "max" background in gray, as seen in the top part of the image.
Right now, I'm simulating this animation by overlaying a white view, and then animating the size of it, so that it looks like the blue is animating to the right. While this works... I need the background gray shape to always be there. With my overlayed white view, this just doesn't work.
Here's the code for drawing the "current code" version:
let context = UIGraphicsGetCurrentContext()
CGContextMoveToPoint(context, 0, self.bounds.height - 6)
CGContextAddLineToPoint(context, self.bounds.width, 0)
CGContextAddLineToPoint(context, self.bounds.width, self.bounds.height)
CGContextAddLineToPoint(context, 0, self.bounds.height)
CGContextSetFillColorWithColor(context,UIColor(red: 37/255, green: 88/255, blue: 120/255, alpha: 1.0).CGColor)
CGContextDrawPath(context, CGPathDrawingMode.Fill)
How can I animate the blue part from left to right, while keeping the gray "max" portion of the graph always visible?
drawRect is producing still picture. To get animation you're saying about I'd recommend the following:
Use CoreAnimation to produce animation
Use UIBezierPath to make a shape you need
Use CALayer's mask to animate within required shape
Here is example code for Playground:
import UIKit
import QuartzCore
import XCPlayground
let view = UIView(frame: CGRect(x: 0, y: 0, width: 120, height: 40))
XCPlaygroundPage.currentPage.liveView = view
let maskPath = UIBezierPath()
maskPath.moveToPoint(CGPoint(x: 10, y: 30))
maskPath.addLineToPoint(CGPoint(x: 10, y: 25))
maskPath.addLineToPoint(CGPoint(x: 100, y: 10))
maskPath.addLineToPoint(CGPoint(x: 100, y: 30))
maskPath.closePath()
let maskLayer = CAShapeLayer()
maskLayer.path = maskPath.CGPath
maskLayer.fillColor = UIColor.whiteColor().CGColor
let rectToAnimateFrom = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 97, height: 40))
let rectToAnimateTo = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 0, height: 40))
let layerOne = CAShapeLayer()
layerOne.path = maskPath.CGPath
layerOne.fillColor = UIColor.grayColor().CGColor
let layerTwo = CAShapeLayer()
layerTwo.mask = maskLayer
layerTwo.fillColor = UIColor.greenColor().CGColor
view.layer.addSublayer(layerOne)
view.layer.addSublayer(layerTwo)
let animation = CABasicAnimation(keyPath: "path")
animation.fromValue = rectToAnimateFrom.CGPath
animation.toValue = rectToAnimateTo.CGPath
animation.duration = 1
animation.repeatCount = 1000
animation.autoreverses = true
layerTwo.addAnimation(animation, forKey: "Nice animation")
In your code, I only see you draw the graphic once, why not draw gray part first and then draw the blue part.
I don't think it is efficient enough to implement animation in drawRect function.
You can take a look at Facebook's Shimmer Example, it simulate the effect of iPhone unlock animation. It uses a mask layer. The idea could also work in your example.
Also, Facebook's pop framework could simplify your work.

Resources