Skip to content

Instantly share code, notes, and snippets.

@vegather
Last active February 26, 2024 16:06
Show Gist options
  • Save vegather/07993d15c83ffcd5182c8c27f1aa600b to your computer and use it in GitHub Desktop.
Save vegather/07993d15c83ffcd5182c8c27f1aa600b to your computer and use it in GitHub Desktop.
A simple view to animate in and out a blurry overlay. Use .blurIn() and .blurOut() to animate the blur. User interaction is passed through when the view is not blurry. NOTE: If you use storyboards, you need to drag out a UIVisualEffectView and set the class. It doesn't work if you drag out a plain old UIView.
class BlurryOverlayView: UIVisualEffectView {
private var animator: UIViewPropertyAnimator!
private var delta: CGFloat = 0 // The amount to change fractionComplete for each tick
private var target: CGFloat = 0 // The fractionComplete we're animating to
private(set) var isBlurred = false
private var displayLink: CADisplayLink!
override init(effect: UIVisualEffect?) {
super.init(effect: effect)
setup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// Common init
private func setup() {
effect = nil // Starts out with no blur
isHidden = true // Enables user interaction through the view
// The animation to add an effect
animator = UIViewPropertyAnimator(duration: 1, curve: .easeInOut) {
self.effect = UIBlurEffect(style: .light)
}
animator.pausesOnCompletion = true // Fixes background bug
// Using a display link to animate animator.fractionComplete
displayLink = CADisplayLink(target: self, selector: #selector(tick))
displayLink.isPaused = true
displayLink.add(to: .main, forMode: .commonModes)
}
func blurIn(amount: CGFloat = 0.2, duration: TimeInterval = 0.3) {
guard isBlurred == false else { return }
isHidden = false // Disable user interaction
target = amount
delta = amount / (60 * CGFloat(duration)) // Assuming 60hz refresh rate
// Start animating fractionComplete
displayLink.isPaused = false
}
func blurOut(duration: TimeInterval = 0.3) {
guard isBlurred else { return }
target = 0
delta = -1 * animator.fractionComplete / (60 * CGFloat(duration)) // Assuming 60hz refresh rate
// Start animating fractionComplete
displayLink.isPaused = false
}
@objc private func tick() {
animator.fractionComplete += delta
if isBlurred && animator.fractionComplete <= 0 {
// Done blurring out
isBlurred = false
isHidden = true
displayLink.isPaused = true
} else if isBlurred == false && animator.fractionComplete >= target {
// Done blurring in
isBlurred = true
displayLink.isPaused = true
}
}
}
@jiwonsim
Copy link

Thank you for creating such wonderful code! But i have a question about blurOut function. I have one View calling BlurryOverlayView and it called blurOut function after 0.5 seconds calling blurIn. But there was some delay loading UIView, and in this case, BlurryOverlayView is not blurred out. So I called blurOut manually if isBlurred is true, but it doesn't work. Do you have any solution?

@minguking
Copy link

Legend

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment