如何淡入淡出 UIVisualEffectView and/or UIBlurEffect?

How to fade a UIVisualEffectView and/or UIBlurEffect in and out?

我想淡入淡出带有 UIBlurEffect 的 UIVisualEffectsView:

var blurEffectView = UIVisualEffectView()
blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))

我在 UIButton 调用的函数中使用普通动画来淡入,淡出也是如此,但是 .alpha = 0 & hidden = true:

blurEffectView.hidden = false
UIView.animate(withDuration: 1, delay: 0, options: .curveEaseOut) {
    self.blurEffectView.alpha = 1
}

现在,双向淡入淡出确实有效,但在淡入时出现错误 out:

<UIVisualEffectView 0x7fdf5bcb6e80> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.

问题

如何成功地使 UIVisualEffectView 淡入和淡出而不 破坏 它并进行淡入淡出过渡?

备注

UIVisualEffectView 的 alpha 必须始终为 1。我认为您可以通过设置背景颜色的 alpha 来实现效果。

来源:https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIVisualEffectView/index.html

除了控制台中的警告外,您可以毫无问题地更改视觉效果视图的 alpha。视图可能看起来只是部分透明,而不是模糊。但如果您只是在动画期间更改 alpha,这通常不是问题。

您的应用不会因此崩溃或被拒绝。在真实设备(或八台)上测试它。如果您对它的外观和性能感到满意,那很好。 Apple 只是警告您,它的外观或性能可能不如 alpha 值为 1 的视觉效果视图。

只是一个解决方法 - 将 UIVisualEffectView 放入容器视图并为该容器更改 alpha 属性。这种方法在 iOS 9 上非常适合我。似乎它在 iOS 10.

上不再有效

您可以拍摄静态底层视图的快照,并在不影响模糊视图的不透明度的情况下淡入淡出。假设一个 blurView 的 ivar:

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()
    } )
}

我认为这是 iOS9 中的新功能,但您现在可以在动画块中设置 UIVisualEffectView 的效果:

let overlay = UIVisualEffectView()
// Put it somewhere, give it a frame...
UIView.animate(withDuration: 0.5) {
    overlay.effect = UIBlurEffect(style: .light)
}

将其设置为 nil 以删除。

非常重要 - 在模拟器上进行测试时,请确保将模拟器的图形质量覆盖设置为高质量,这样才能正常工作。

我最终得到以下解决方案,为 UIVisualEffectView 和内容使用单独的动画。我使用 viewWithTag() 方法获取对 UIVisualEffectView.

UIView 的引用
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 }

我更喜欢改变 alpha 的单个动画,但这避免了错误并且似乎也能正常工作。

我制作了一个 alpha 为 0 的 uiview,并将 blurview 添加为它的子视图。所以我可以 hide/show 或用动画圆角它。

Apple documentation(当前)状态...

When using the UIVisualEffectView class, avoid alpha values that are less than 1.

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.

我认为这里缺少一些重要的上下文...

我建议的目的是避免持久视图的 alpha 值小于 1。以我的拙见,这不适用于视图的动画。

我的观点 - 我建议动画可以接受小于 1 的 alpha 值。

终端消息指出:

UIVisualEffectView is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.

仔细阅读这个,效果会出现被打破。我对此的看法是:

  • 明显的中断只对持久不变的视图真正重要;
  • alpha 值小于 1 的持久/不变 UIVisualEffect 视图将不会按 Apple 的预期/设计呈现;和
  • 终端中的消息不是错误,只是警告。

为了扩展@jrturton 上面帮助我解决问题的答案,我会添加...

要淡出 UIVisualEffect 使用以下 (Objective-C) 代码:

UIView.animateWithDuration(1.0, animations: {
//  EITHER...
    self.blurEffectView.effect = UIBlurEffect(nil)
//  OR...
    self.blurEffectView.alpha = 0
}, completion: { (finished: Bool) -> Void in
    self.blurEffectView.removeFromSuperview()
} )

我成功地使用了两种方法:将 effect 属性 设置为 nil 并将 alpha 属性 设置为 0

请注意,将 effect 设置为 nil 会在动画结束时创建一个 "nice flash"(为了更好的描述),同时设置 alpha0 创建平滑过渡。

(让我知道任何语法错误...我用 obj-c 编写。)

_visualEffectView.contentView.alpha = 0;

要改变 UIVisualEffectView 的 alpha,你应该改变 _visualEffectView.If 的 contentView 你改变 _visualEffectView 的 alpha,你会得到这个

<UIVisualEffectView 0x7ff7bb54b490> is being asked to animate its opacity. This will cause the effect to appear broken until opacity returns to 1.

如果您想淡入 UIVisualEffectView - ios10 使用 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]];
    }];

然后你可以设置百分比

[animator setFractionComplete:percent];

对于 ios9 你可以使用 alpha 组件

这是我最终得到的解决方案,它适用于 iOS10 和使用 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
            }
        }
    }

}

您还可以check this gist查找示例用法。

通常,我只想在屏幕上呈现视图控制器时制作模糊动画,并想模糊呈现的视图控制器。这是一个将 blur() 和 unblur() 添加到视图控制器以促进这一点的扩展:

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()
           }
       }
   }
}

当然,您可以让用户选择效果样式、修改持续时间、在动画完成时调用一些东西、在 blur() 中标记添加的视觉效果视图以确保它是唯一的,从而使它更健壮当你 unblur() 等时删除,但到目前为止我还没有发现需要做这些事情,因为这往往是一种 "fire and forget" 类型的操作。

我刚遇到这个问题,我解决这个问题的方法是将 UIVisualEffectsView 放在 UIView 中,并为该 UIView 的 alpha 设置动画。

这很好用,只是一旦 alpha 低于 1.0,它就会变成纯白色,看起来非常刺耳。为了解决这个问题,您必须设置 UIView 的图层 属性 containerView.layer.allowsGroupOpacity = false,这将防止它闪烁白色。

现在您可以使用它的 alpha 属性 动画 in/fade 包含视觉效果视图和任何其他子视图的 UIView,而不必担心任何图形故障或记录警告消息。

根据@cc 的回答,我修改了他的扩展以模糊视图

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()
            }
        }
    }
}

改进@Tel4tel 和@cc 响应,这里是一个带有参数的扩展和一个简短的解释。

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()
            }
        }
    }
}

然后你可以像这样使用它: view.blur(duration: 5.0, effect: .light) 要么 view.unblur(duration: 3.0)

切记不要在 viewDidLoad() 中使用它,因为它会覆盖动画。 此外,当 运行 在模拟器上时,将图形调到更高级别以便能够看到动画(调试 > 图形质量覆盖 > 高质量)。