I wrote my function to insert a layer (addsubview) to my main view, i gave it vars to manage size and colors for its gradient background with an opacity at 1. I inserted with an index etc...
Now, i would like to manage the opacity of this gradient (background) sublayer through actions :
I'm able to remove it, play with the general opacity of the subview... but impossible to target the opacity of this sublayer at index 0.
Any idea ?
in my viewdidLoad func :
func insertHeader () {
self.view.addSubview(TopMenuView)
TopMenuView.frame.size.width = self.view.bounds.size.width
let gradient:CAGradientLayer = CAGradientLayer()
let colorTop = UIColor(RGBa).cgColor
let colorBottom = UIColor(RGBa).cgColor
//etc
gradient.opacity = 1.0
TopMenuView.layer.insertSublayer(gradient, at: 0)
}
And later, unable to target the opacity of this sublayer gradient...
I can manage the whole opacity of the TopMenuView.layer but not its "background gradient layer"
You should be able to create a reference to the gradient layer...
class ViewController: UIViewController {
var topMenuGradient = CAGradientLayer()
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(TopMenuView)
TopMenuView.frame.size.width = self.view.bounds.size.width
let colorTop = UIColor(RGBa).cgColor
let colorBottom = UIColor(RGBa).cgColor
//etc
topMenuGradient.frame = TopMenuView.bounds
topMenuGradient.opacity = 0.5
TopMenuView.layer.insertSublayer(topMenuGradient, at: 0)
}
#IBAction func btnTapped(_ sender: Any) {
topMenuGradient.opacity = 0.1
}
}
Im creating a blur in my ios app every time you send the app to background. This is what I'm using right now:
var topController : UIViewController = (application.keyWindow?.rootViewController)!
while ((topController.presentedViewController) != nil)
{
topController = topController.presentedViewController!
}
if !UIAccessibilityIsReduceTransparencyEnabled()
{
topController.view.backgroundColor = UIColor.clear
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
// Always fill the view
blurEffectView.frame = topController.view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
topController.view.addSubview(blurEffectView) //if you have more UIViews, use an insertSubview API to place it where needed
}
else
{
topController.view.backgroundColor = UIColor.black
}
My question is. Is there a way I can delete this blur in another viewcontroller (which is inside a tabbarcontroller)?
EDIT: I need to delete the blur in another viewcontroller because my user needs to enter a password and if it is correct it'll delete the blur.
I want to fade a UIVisualEffectsView with a UIBlurEffect in and out:
var blurEffectView = UIVisualEffectView()
blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
I use a normal animation within a function called by a UIButton to fade it in, same for fading out but .alpha = 0 & hidden = true:
blurEffectView.hidden = false
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut) {
self.blurEffectView.alpha = 1
}
Now, fading in both directions does work but it gives me an error when fading out:
<UIVisualEffectView 0x7fdf5bcb6e80> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Question
How do I successfully fade the UIVisualEffectView in and out without breaking it and having a fading transition?
Note
I tried to put the UIVisualEffectView into a UIView and fade that one, no success
I think this is new in iOS9, but you can now set the effect of a UIVisualEffectView inside an animation block:
let overlay = UIVisualEffectView()
// Put it somewhere, give it a frame...
UIView.animate(withDuration: 0.5) {
overlay.effect = UIBlurEffect(style: .light)
}
Set it to nil to remove.
VERY IMPORTANT - When testing this on the simulator, make sure to set your simulator's Graphics Quality Override to High Quality in order for this to work.
The Apple documentation (currently) states...
When using the UIVisualEffectView class, avoid alpha values that are less than 1.
and
Setting the alpha to less than 1 on the visual effect view or any of its superviews causes many effects to look incorrect or not show up at all.
I believe some important context is missing here...
I'd suggest that the intent is to avoid alpha values that are less than 1 for a persistent view. In my humble opinion this does not apply to the animation of a view.
My point - I'd suggest that alpha values less than 1 are acceptable for animations.
The terminal message states:
UIVisualEffectView is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Reading this carefully, the effect will appear to be broken. My points on this being:
the apparent break only really matters for a view that is persistent - not changing;
a persistent / unchanging UIVisualEffect view with an alpha value less than 1 will not present as intended / designed by Apple; and
the message in the terminal is not an error, just a warning.
To extend #jrturton's answer above that helped me solve my problem, I'd add...
To fade out the UIVisualEffect use the following (Objective-C) code:
UIView.animateWithDuration(1.0, animations: {
// EITHER...
self.blurEffectView.effect = UIBlurEffect(nil)
// OR...
self.blurEffectView.alpha = 0
}, completion: { (finished: Bool) -> Void in
self.blurEffectView.removeFromSuperview()
} )
I successfully use both methods: setting the effect property to nil and setting the alpha property to 0.
Note that setting the effect to nil creates a "nice flash" (for want of a better description) at the end of the animation, while setting the alpha to 0 creates a smooth transition.
(Let me know any syntax errors... I write in obj-c.)
Here is the solution that I ended up which works on both iOS10 and earlier versions using Swift 3
extension UIVisualEffectView {
func fadeInEffect(_ style:UIBlurEffectStyle = .light, withDuration duration: TimeInterval = 1.0) {
if #available(iOS 10.0, *) {
let animator = UIViewPropertyAnimator(duration: duration, curve: .easeIn) {
self.effect = UIBlurEffect(style: style)
}
animator.startAnimation()
}else {
// Fallback on earlier versions
UIView.animate(withDuration: duration) {
self.effect = UIBlurEffect(style: style)
}
}
}
func fadeOutEffect(withDuration duration: TimeInterval = 1.0) {
if #available(iOS 10.0, *) {
let animator = UIViewPropertyAnimator(duration: duration, curve: .linear) {
self.effect = nil
}
animator.startAnimation()
animator.fractionComplete = 1
}else {
// Fallback on earlier versions
UIView.animate(withDuration: duration) {
self.effect = nil
}
}
}
}
You can also check this gist to find an example usage.
Just a workaround - put UIVisualEffectView into a container view and change alpha property for that container. That approach works perfectly for me on iOS 9. Seems it no longer works in iOS 10.
You can change the alpha of the visual effects view without any problems, other than the warning in the console. The view may appear as simply partially transparent, rather than blurred. But this usually isn't a problem if you're just changing the alpha during animation.
Your app isn't going to crash or get rejected for this. Test it on a real device (or eight). If you're happy with how it looks and performs, it's fine. Apple's just warning you that it may not look or perform as well as a visual effects view with an alpha value of 1.
You can take a snapshot of a static underlying view, and fade it in and out without touching the opacity of the blur view. Assuming an ivar of blurView:
func addBlur() {
guard let blurEffectView = blurEffectView else { return }
//snapShot = UIScreen.mainScreen().snapshotViewAfterScreenUpdates(false)
let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
view.addSubview(blurEffectView)
view.addSubview(snapShot)
UIView.animateWithDuration(0.25, animations: {
snapShot.alpha = 0.0
}, completion: { (finished: Bool) -> Void in
snapShot.removeFromSuperview()
} )
}
func removeBlur() {
guard let blurEffectView = blurEffectView else { return }
let snapShot = self.view.snapshotViewAfterScreenUpdates(false)
snapShot.alpha = 0.0
view.addSubview(snapShot)
UIView.animateWithDuration(0.25, animations: {
snapShot.alpha = 1.0
}, completion: { (finished: Bool) -> Void in
blurEffectView.removeFromSuperview()
snapShot.removeFromSuperview()
} )
}
If you want to fade in you UIVisualEffectView - for ios10 use UIViewPropertyAnimator
UIVisualEffectView *blurEffectView = [[UIVisualEffectView alloc] initWithEffect:nil];
blurEffectView.frame = self.view.frame;
blurEffectView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIView *blackUIView = [[UIView alloc]initWithFrame:self.view.frame];
[bacgroundImageView addSubview:blackUIView];
[blackUIView addSubview:blurEffectView];
UIViewPropertyAnimator *animator = [[UIViewPropertyAnimator alloc] initWithDuration:4.f curve:UIViewAnimationCurveLinear animations:^{
[blurEffectView setEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
}];
then you can set percent
[animator setFractionComplete:percent];
for ios9 you can use alpha component
The alpha of the UIVisualEffectView always has to be 1. I think you can achieve the effect by setting the alpha of the background color.
Source : https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffectView/index.html
I make an uiview which alpha is 0 and add blurview as subview of that. So i can hide/show or rounding corners it with animation.
I ended up with the following solution, using separate animations for the UIVisualEffectView and the contents. I used the viewWithTag() method to get a reference to the UIView inside the UIVisualEffectView.
let blurEffectView = UIVisualEffectView()
// Fade in
UIView.animateWithDuration(1) { self.blurEffectView.effect = UIBlurEffect(style: .Light) }
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 1 }
// Fade out
UIView.animateWithDuration(1) { self.blurEffectView.effect = nil }
UIView.animateWithDuration(1) { self.blurEffectView.viewWithTag(1)?.alpha = 0 }
I would prefer the single animation changing the alpha, but this avoids the error and seems to work just as well.
I just had this problem and the way I got around it was to house the UIVisualEffectsView in a UIView, and animate that UIView's alpha.
This worked well, except that it as soon as the alpha changed below 1.0 it turned to a solid white and looked very jarring. In order to get around this, you must set the UIView's layer property containerView.layer.allowsGroupOpacity = false and this will prevent it from flashing white.
Now you can animate in/fade out the UIView containing the visual effects view and any other subviews using it's alpha property and not have to worry about any graphical glitches or it logging a warning message.
_visualEffectView.contentView.alpha = 0;
To change the alpha of UIVisualEffectView, you should change the contentView of _visualEffectView.If you change alpha of _visualEffectView, you will get this
<UIVisualEffectView 0x7ff7bb54b490> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.
Usually, I only want to animate a blur when I'm presenting a view controller over the screen and want to blur the presenting view controller. Here's an extension that adds blur() and unblur() to a view controller in order to facilitate that:
extension UIViewController {
func blur() {
// Blur out the current view
let blurView = UIVisualEffectView(frame: self.view.frame)
self.view.addSubview(blurView)
UIView.animate(withDuration:0.25) {
blurView.effect = UIBlurEffect(style: .light)
}
}
func unblur() {
for childView in view.subviews {
guard let effectView = childView as? UIVisualEffectView else { continue }
UIView.animate(withDuration: 0.25, animations: {
effectView.effect = nil
}) {
didFinish in
effectView.removeFromSuperview()
}
}
}
}
You can of course make this more robust by letting the user choose the effect style, modify the duration, call something when the animation is completed, tag the added visual effect view in blur() to ensure it's the only one removed when you unblur(), etc., but I haven't found the need to do these things so far, since this tends to be a "fire and forget" type of operation.
based on #cc's answer i modified his extension to blur a view
extension UIView {
func blur() {
// Blur out the current view
let blurView = UIVisualEffectView(frame: self.bounds)
self.addSubview(blurView)
UIView.animate(withDuration:0.25) {
blurView.effect = UIBlurEffect(style: .dark)
}
}
func unblur() {
for childView in subviews {
guard let effectView = childView as? UIVisualEffectView else { continue }
UIView.animate(withDuration: 2.5, animations: {
effectView.effect = nil
}) {
didFinish in
effectView.removeFromSuperview()
}
}
}
}
Improving #Tel4tel and #cc response, here is an extension with parameters and a brief explanation.
extension UIView {
// Perform a blur animation in the whole view
// Effect tone can be .light, .dark, .regular...
func blur(duration inSeconds: Double, effect tone: UIBlurEffectStyle) {
let blurView = UIVisualEffectView(frame: self.bounds)
self.addSubview(blurView)
UIView.animate(withDuration: inSeconds) {
blurView.effect = UIBlurEffect(style: tone)
}
}
// Perform an unblur animation in the whole view
func unblur(duration inSeconds: Double) {
for childView in subviews {
guard let effectView = childView as? UIVisualEffectView else { continue
}
UIView.animate(withDuration: inSeconds, animations: {
effectView.effect = nil
}){
didFinish in effectView.removeFromSuperview()
}
}
}
}
Then you can use it like:
view.blur(duration: 5.0, effect: .light)
or
view.unblur(duration: 3.0)
Remember to NOT use it in viewDidLoad() as it will override the animation.
Also, when running on a Simulator, turn the graphics to the Higher level to be able to see the animation (Debug > Graphics Quality Override > High Quality).
I have a drawing app that uses CALayer sublayers for the actual drawing of an image. I currently have an IBAction that contains code to remove the sublayers from the superlayer. However everytime I run it, I get a BAD ACCESS error that crashes my app. I'm wondering why it won't allow me to remove the sublayers. Also, in theory, this approach would remove all of the layers entirely. I would ideally want it to only undo the last layers that were drawn. Any suggestions on what I should do? Thanks.
var locale: CALayer {
return layerView.layer
}
#IBAction func undoButton(sender: AnyObject) {
var sublayers = self.view.layer.sublayers
for layer in sublayers {
layer.removeFromSuperlayer()
}
}
func setUpLayer() -> CALayer {
locale.contents = image
locale.contentsGravity = kCAGravityCenter
return locale
}
func subLayerDisplay() {
var newLayer = CALayer()
var tempRect = CGRectMake(prevLocation.x, prevLocation.y, 50, 50)
newLayer.frame = tempRect
newLayer.contents = image
self.view.layer.insertSublayer(newLayer, below: locale)
}
To sum up, you can add your layer to a view, and then deal with the view, which will make life much more easier.
When you add the button, you can do
view.tag = 1
self.addSubView(view)
When you want to remove it
let view = self.viewWithTag(1) as! UIView
view.removeFromSuperView()
All,
I have an image and then I display it on the screen and then I go to a function to blur the image. Then I run another function to add a box to it. but it doesn't show the blur and the box and this is because of the addSubView and the insertSubView - I presume. Basically I cannot put both the blur and the box on the view. If i uncomment out the addBox it doesn't show the blur. Can anyone help with my understanding of addSubView and InsertSubView (array).
Here is my code :
class ViewController: UIViewController {
var Box : UIView?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let bananaImage : UIImage = UIImage(named: "edify-backgound.png")
var imageV : UIImageView = UIImageView(image: bananaImage)
imageV.frame = CGRectMake(0, 0, bananaImage.size.width, bananaImage.size.height)
imageV.center = self.view.center
self.view.addSubview(imageV)
blur()
//addBox(CGRectMake(200, 300, 30, 30))
}
func addBox(location: CGRect)
{
let newBox = UIView(frame: location)
newBox.backgroundColor = UIColor.clearColor()
self.view.insertSubview(newBox, atIndex: 1)
Box = newBox
}
func blur()
{
var blur = UIVisualEffectView(effect: UIBlurEffect(style: .Light)) as UIVisualEffectView
blur.frame = self.view.frame
self.view.addSubview(blur)
}
Part of the problem is that you have no way of knowing whether addBox is doing anything or not. Here is your code:
let newBox = UIView(frame: location)
newBox.backgroundColor = UIColor.clearColor()
self.view.insertSubview(newBox, atIndex: 1)
A view that consists of nothing but a clear background color is completely invisible. So you see nothing - which, as Sherlock Holmes says, is exactly what you may expect to see.