UIVisualEffectView with CAGradientLayer not working on iOS 10 - ios

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

Related

Apply CAGradientLayer to layer.borderColor

I'm trying to implement a underlined UITextField with a gradient. Therefore I created a extension with a function underlined().
To get a gradient, I created a CAGradientLayer and made these customizations:
func underlined(){
let color = UIColor(red: 11/255, green: 95/255, blue: 244/255, alpha: 1).cgColor
let sndColor = UIColor(red: 106/255, green: 178/255, blue: 255/255, alpha: 1).cgColor
let gradient: CAGradientLayer = CAGradientLayer()
gradient.colors = [color, sndColor]
gradient.locations = [0.0, 1.0]
gradient.startPoint = CGPoint(x: 0, y: 0)
gradient.endPoint = CGPoint(x: 1, y: 0)
let width = CGFloat(2.0)
gradient.frame = CGRect(x: 0, y: self.frame.size.height - width, width: self.frame.size.width, height: self.frame.size.height)
gradient.borderWidth = width
self.layer.insertSublayer(gradient, at: 0)
self.layer.masksToBounds = true
}
A underline is being displayed, but it solely solid black - I've tried to change the colors, but it remains black (Issue outdated - see edit).
Does anybody see the issue?
Edit:
Adding gradient.borderColor = UIColor.blue.cgColor let me change the color of the line - but how can I apply the gradient on the border color?
Remove the below line from your code:
gradient.borderWidth = width
Screenshot:
Your code was not working because, the borderWidth is covering whole of the gradient frame.Try setting the borderColor, then you'll see the difference.
gradient.borderColor = UIColor.red.cgColor
Let me know if you still face any issues.

Add gradient layer to uiviewcontroller uiview

I am trying to create a simple gradient for the background of a UIViewController. My code is:
let gradientLayer = CAGradientLayer()
gradientLayer.frame = view.bounds
let bottomColor = UIColor(hue: 208 / 360, saturation: 82 / 100, brightness: 0.9, alpha: 1)
let topColor = UIColor(hue: 208 / 360, saturation: 41 / 100, brightness: 0.9, alpha: 1)
gradientLayer.colors = [bottomColor, topColor]
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.1)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
view.layer.addSublayer(gradientLayer)
The gradient does not show. When I look in the view debugger, I do not see it. I check the frame of the view and it looks ok
x: 0, y:0, width: 375, height: 667
I've tried the view.layer.insert at method as well, but that did not work. If I do
view.layer.backgroundColor.orange
then I do see an orange background. Am I missing something?
Change
gradientLayer.colors = [bottomColor, topColor]
to
gradientLayer.colors = [bottomColor.cgColor, topColor.cgColor]

Swift: Adding gradient layer to button. Layer length error

let gradient: CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
The resulting gradient runs off goes beyond the button's frame. Why does this happen?
Probably you are setting gradient layer in viewDidLoad() or viewWillAppear(). At that time controller did not calculated views sizes. At time you add this sublayer button size was not calculated yet, so sublayer size is wrong. So you should fix next things:
First of all you should add gradient at viewDidAppear(), at this point all view's sizes are calculated.
Second, you should use layer.insertSublayer(layer, atIndex:index) instead of addSublayer(layer). Because in your case sublayer will hide buttons native layer (title, background...)
You should recalculate your sublayer size in viewDidLayoutSubviews(). Because when your button will change it's size (while rotating for example), sublayer will not change it's frame, so you should change it by yourself.
Add clipsToBounds and cornerRadius to loginbutton. That should fix the problem.
loginButton.clipsToBounds = true
loginButton.layer.cornerRadius = 5;
Is your Login Button's frame correct?It seems correct when I reproduce
let loginButton = UIButton(frame: CGRect(x: 10, y: 50, width: 300, height: 30))
self.view.addSubview(loginButton)
let gradient:CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(red: 112.0/255.0, green: 219.0/255.0, blue: 155.0/255.0, alpha: 1.0).CGColor
let colorBottom = UIColor(red: 86.0/255.0, green: 197.0/255.0, blue: 238.0/255.0, alpha: 1.0).CGColor
gradient.colors = [colorTop, colorBottom]
gradient.startPoint = CGPoint(x: 0.0, y: 0.5)
gradient.endPoint = CGPoint(x: 1.0, y: 0.5)
gradient.frame = loginButton.bounds
gradient.cornerRadius = 5
loginButton.layer.addSublayer(gradient)
And it appear like below

Gradiant in playground different from simulator

I tried to make a gradiant using playground then make it into a subclass of uiview to use it in my codebase.
The playground code is like this:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 640, height: 41
))
let gradient: CAGradientLayer = CAGradientLayer(layer: view.layer)
gradient.frame = view.frame
let lightColor = UIColor(red: 157/256, green: 157/256, blue: 154/256, alpha: 1)
let darkColor = UIColor(red: 108/256, green: 104/256, blue: 104/256, alpha: 1)
gradient.colors = [lightColor.CGColor, darkColor.CGColor]
gradient.locations = [0.6 , 0.4]
gradient.startPoint = CGPoint(x: 1, y: 0.55)
gradient.endPoint = CGPoint(x: 0, y: 0.45)
view.layer.insertSublayer(gradient, atIndex: 0)
view //displayed
And display like this:
I tried to make it into my codebase with:
class GradiantView : UIView {
override var frame: CGRect {
didSet {
let gradient: CAGradientLayer = CAGradientLayer(layer: layer)
gradient.frame = bounds
let lightColor = UIColor(red: 157/256, green: 157/256, blue: 154/256, alpha: 1)
let darkColor = UIColor(red: 108/256, green: 104/256, blue: 104/256, alpha: 1)
gradient.colors = [lightColor.CGColor, darkColor.CGColor]
gradient.locations = [0.6 , 0.4]
gradient.startPoint = CGPoint(x: 1, y: 0.55)
gradient.endPoint = CGPoint(x: 0, y: 0.45)
layer.insertSublayer(gradient, atIndex: 0)
}
}
}
However, the result is like this: (no gradiant applied)
What happened and how to fix it ?
I inverted the locations and it seems to work.
I welcome any explanation on the difference between simulator and playground.
gradientLayer.locations = [0.4 , 0.6]

CAGradientLayer Mask add Opacity in Swift

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?

Resources