Last active
February 26, 2024 16:06
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} |
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?
Legend
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is AWESOME! Thank you!