使用模糊效果呈现视图控制器

11

我正在使用模糊背景效果以模态方式呈现视图控制器。在iOS 10 / XCode 8中,我的动画遇到了问题。这是演示代码:

let modalVC = ModalViewController(nibName: "ModalViewController", bundle: nil)
modalVC.modalTransitionStyle = .CrossDissolve
modalVC.modalPresentationStyle = .OverFullScreen
presentViewController(modalVC, animated: true, completion: nil)

在ModalViewController的viewDidLoad()函数中添加模糊效果:

let blurEffect = UIBlurEffect(style: .Light)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
blurEffectView.autoresizingMask = [.FlexibleWidth, .FlexibleHeight]

view.addSubview(blurEffectView)
view.sendSubviewToBack(blurEffectView)

ModalViewController有一个清晰的背景,并且我添加了一个带有暗模糊效果的BlurEffectView。我尝试在代码中使用之前的片段和在界面生成器中。

在iOS 8和9上,.CrossDissolve转换会处理"淡入淡出",但在iOS 10(包括设备和模拟器)上测试后,视图将显示为带半透明黑色背景而不是模糊效果。

.CrossDissolve动画完成后,背景颜色会更改为实际的模糊效果背景。这是为什么?有任何想法吗?

也尝试在模态视图控制器的viewDidLoad()的开头和结尾处添加layoutIfNeeded(),但没有成功。我正在使用swift 2.3。


我也发现了这种烦人的行为变化。你尝试过关闭转场样式并手动处理动画吗? - Johannes
尝试了一下,看起来更好了,但模糊仍然有轻微的延迟。只是不那么明显了。 - Johannes
有一个辅助功能可以禁用模糊效果,适用于那些忘记了这一点的人。此外,为 OP 点赞,我也遇到了同样的问题。我有一个带有自定义单元格的表视图,位于呈现 VC 的后面。模糊效果仅适用于跨淡入淡出动画结束时的背景内容,然后突然模糊呈现的 VC。 - cloudcal
3个回答

11
嗨,

您需要创建一个新的 UIViewControllerAnimatedTransitioning

然后在 animateTransition(using transitionContext: UIViewControllerContextTransitioning) 中编写您的动画代码。

现在在 iOS 10 中,您可以使用 UIViewPropertyAnimator 来动画显示 UIVisualBlurEffectBlurRadius

结果:enter image description here

这里有一个用法示例:https://github.com/PierrePerrin/PPBlurModalPresentation

第一步

您需要创建模糊过渡效果。

    class BlurModalPresentation: NSObject,UIViewControllerAnimatedTransitioning {


        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval{

            return 0.5
        }

//This is the blur view used for transition
        var blurView = UIVisualEffectView(effect: UIBlurEffect(style: UIBlurEffectStyle.light))
        var destinationView : UIView!
        var animator: UIViewPropertyAnimator?

        // This method can only  be a nop if the transition is interactive and not a percentDriven interactive transition.
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning){

            let containerView = transitionContext.containerView

            _ = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)
            let toVc = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)

            destinationView = toVc!.view
            destinationView.alpha = 0.0

//Here we add the blur view and set it effect to nil
            blurView.effect = nil
            blurView.frame = containerView.bounds

            self.blurTransition(transitionContext) { 

                self.unBlur(transitionContext, completion: { 

                    self.blurView.removeFromSuperview()
                    transitionContext.completeTransition(true)
                })
            }

            containerView.addSubview(toVc!.view)
            containerView.addSubview(blurView)
        }

        //This methods add the blur to our view and our destinationView
        func blurTransition(_ context : UIViewControllerContextTransitioning,completion: @escaping () -> Void){

            UIViewPropertyAnimator.runningPropertyAnimator(withDuration: self.transitionDuration(using: context)/2, delay: 0, options: UIViewAnimationOptions.curveLinear, animations: {

                self.destinationView.alpha = 0.5
                  self.blurView.effect = UIBlurEffect(style: UIBlurEffectStyle.light)
            }, completion: { (position) in
                completion()
            })

        }
        //This Method remove the blur view with an animation
        func unBlur(_ context : UIViewControllerContextTransitioning,completion: @escaping () -> Void){

            UIViewPropertyAnimator.runningPropertyAnimator(withDuration: self.transitionDuration(using: context) / 2, delay:0, options: UIViewAnimationOptions.curveLinear, animations: {

                self.destinationView.alpha = 1.0
                self.blurView.effect = nil
            }, completion: { (position) in
                completion()
            })
        }

    }

然后

您需要在ViewController中设置转场代理:

import UIKit

class ViewController: UIViewController,UIViewControllerTransitioningDelegate {

    let blurModalPresentation = BlurModalPresentation()

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    func showVC(){

        let str = self.storyboard!
        let vc = str.instantiateViewController(withIdentifier: "YourViewControllerIdentifier")
        vc.transitioningDelegate = self
        self.present(vc, animated: true, completion: nil)
    }


    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
    }

    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning?{

        return blurModalPresentation
    }


    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning?{

        return blurModalPresentation
    }

}

1

1

首先,我认为这个解决方案只是一个临时的解决办法,因为我必须假设这种新引入的行为是一个错误,并且将在未来的更新中得到修复。这使得模糊效果在动画期间弹出,而不是之后,因此略微不太显眼。仍然不如iOS 9及以前的版本好,但稍微好一些。

  1. Present view controller without animation:

    presentViewController(modalVC, animated: false, completion: nil)
    
  2. Hide your view from the beginning:

    override func viewDidLoad() {
        super.viewDidLoad()
    
        view.alpha = 0
    }
    
  3. Apply animations manually:

    override func viewWillAppear(animated: Bool) {
        super.viewWillAppear(animated)
    
        UIView.animateWithDuration(0.25) {
            self.view.alpha = 1
        }
    }
    
    override func viewWillDisappear(animated: Bool) {
        super.viewWillDisappear(animated)
    
        UIView.animateWithDuration(0.25) {
            self.view.alpha = 0
        }
    }
    

是的,目前看来这似乎是唯一的解决方法,但额外的延迟有点让人烦恼。在接受之前,我会把问题保持开放一段时间,希望有人能提出其他的解决方案。 - Fdo
我理解你的意思,但你可能需要编辑你的第一步骤为presentViewController(modalVC, animated: false, completion: nil) - Fdo
糟糕,谢谢@Fdo!将“animated”更改为“false”。我并不希望这个答案被选为解决方案,因为它远非完美。同时也希望有人能提出更好的解决方案! - Johannes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接