【iOS】实现可自定义模糊度的毛玻璃及动画效果

在iOS中,通常是使用UIVisualEffectView+UIBlurEffect实现毛玻璃效果,但是苹果并没有提供修改模糊度的API。网上很多都是修改其alpha值实现所谓“模糊度改变”的效果,这种只是自欺欺人的做法,并非真的修改了模糊度。

Demo地址:JPBlurView

实现毛玻璃动画

虽说没有修改模糊度的API,但是有毛玻璃的展示和隐藏动画:

let effectView = UIVisualEffectView(effect: nil)

// 毛玻璃展示动画
UIView.animate(withDuration: 0.3) {
    effectView.effect = UIBlurEffect(style: systemThinMaterialDark)
}

// 毛玻璃隐藏动画
UIView.animate(withDuration: 0.3) {
    effectView.effect = nil
}

只要对UIVisualEffectVieweffect属性的setter方法包裹一层动画,就可以实现毛玻璃动画了:

effect.gif

控制毛玻璃动画进度

仔细观察,这个毛玻璃效果从无到有这个过程,不就是模糊度从0到1的过程吗?那么只要能控制这个毛玻璃动画的进度,就相当于自定义模糊度了。这种情况就是UIViewPropertyAnimator登场的时候了。

UIViewPropertyAnimator是iOS中的一个动画类,用于创建和管理视图动画。它提供了一种强大且灵活的方式来创建自定义的交互式动画:可以定义动画的起始状态、结束状态和持续时间,还可以通过控制动画的进度来实现交互性。

更多的介绍这里就不赘述了,网上很多资料可以参考,这里简单实现一下:

let effectView = UIVisualEffectView(effect: nil)

animator = UIViewPropertyAnimator(duration: 0, curve: .linear, animations: { [weak effectView] in
    effectView?.effect = UIBlurEffect(style: systemThinMaterialDark)
})

animator.fractionComplete = 0

这样,通过修改animatorfractionComplete属性从而实现可自定义模糊度的毛玻璃效果了:

JPBlurView_1.gif

实现可自定义模糊度动画

虽然UIViewPropertyAnimator可以控制动画进度,但是并不能实现自定义模糊度的动画。

例如如果想通过UIViewPropertyAnimator实现模糊度0.2~0.7的动画是不行的,一旦动画开始了就会直达1.0,不能执行到某个值自动停下,除非对fractionComplete属性进行监听,我个人觉得这种做法很麻烦。

有没有一种模拟动画并且能返回动画进度的方法呢?这样我就能通过这个实时动画进度更新fractionComplete属性就可以了。

很幸运,有!那就是pop

POPPropertyAnimation有个POPAnimatableProperty属性,可以实时监听动画的进度,而且是带有动画曲线的进度,并且不需要真的对某个视图添加动画,简单来说就是能模拟一个动画,十分好用。

我这里选择使用POPBasicAnimation来实现,因为POPBasicAnimation能自定义动画时长和动画曲线,能满足基本的动画效果:

// 创建一个基类专门来监听进度
let animObserver = NSObject()

let animation = POPBasicAnimation()
animation.fromValue = 0.2
animation.toValue = 0.7
animation.duration = 0.3
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
// 监听实时动画进度
animation.property = POPAnimatableProperty.property(withName: "JPProperty") { [weak animator] prop in
    prop?.writeBlock = { (_, values) in
        guard let values = values else { return }
        let value = values[0]
        // 刷新模糊度
        animator?.fractionComplete = value
    }
} as? POPAnimatableProperty

// 开始(模拟)动画
animObserver.pop_add(animation, forKey: "JPAnimation")

这样就能实现可自定义模糊度动画了:

JPBlurAnimationView_1.gif

需要注意的地方

  1. 一旦使用UIViewPropertyAnimator暂停了动画(没有开启或还没结束),必须在退出页面前让动画结束,否则会崩溃!
animator.stopAnimation(true)
  1. 同样,如果使用UIViewPropertyAnimator暂停了动画,App一旦进入后台模式就会失效(挂起时不会),返回前台时需要重新设置一下:
@objc func willEnterForegroundHandle() {
    guard animator.state != .active else { return }
    animator.stopAnimation(true)
    resetAnimator()
}

实现可交互的毛玻璃背景

结合上面的做法,就可以在浏览大图的背景实现这种可交互的毛玻璃效果了:

JPBlurAnimationView_2.gif

Demo地址:JPBlurView

猜你喜欢

转载自juejin.im/post/7240793907633946685