Skip to content

Commit e45cc8f

Browse files
committed
fix: improve blur animation handling on view lifecycle events
1 parent f9334d4 commit e45cc8f

1 file changed

Lines changed: 28 additions & 20 deletions

File tree

ios/Views/BlurEffectView.swift

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,37 +30,45 @@ class BlurEffectView: UIVisualEffectView {
3030
setupBlur()
3131
}
3232

33+
override func didMoveToWindow() {
34+
super.didMoveToWindow()
35+
guard window != nil else { return }
36+
// UIKit resumes paused CAAnimations when a view re-joins a window
37+
// (e.g. after modal dismiss + re-present). If the animation plays
38+
// toward its end state the blur drifts to full intensity. Re-pause
39+
// and re-set the fraction here to lock it back to our intended value.
40+
// pausesOnCompletion = true (set in setupBlur) ensures the animator
41+
// stays .active even if it reaches fraction 1.0, so this is always safe.
42+
animator?.pauseAnimation()
43+
animator?.fractionComplete = intensity
44+
}
45+
3346
private func setupBlur() {
34-
// Clean up existing animator
35-
if let animator = animator {
36-
animator.stopAnimation(true)
37-
animator.finishAnimation(at: .current)
47+
if let existing = animator, existing.state == .active {
48+
existing.stopAnimation(true)
3849
}
3950
animator = nil
4051

41-
// Reset effect
4252
effect = nil
4353

44-
// Create new animator
45-
animator = UIViewPropertyAnimator(duration: 1, curve: .linear)
46-
animator?.addAnimations { [weak self] in
54+
let newAnimator = UIViewPropertyAnimator(duration: 1, curve: .linear)
55+
newAnimator.addAnimations { [weak self] in
4756
self?.effect = UIBlurEffect(style: self?.blurStyle ?? .systemMaterial)
4857
}
49-
50-
// Set intensity and immediately freeze the animator so the blur is
51-
// applied synchronously. The previous async dispatch caused a one-frame
52-
// window where the wrong intensity was visible, leading to flickers on
53-
// navigation (issue #101) and fallback-color flashes with many instances
54-
// on iOS 26.2 (issue #85).
55-
animator?.fractionComplete = intensity
56-
animator?.stopAnimation(true)
57-
animator?.finishAnimation(at: .current)
58+
// pausesOnCompletion: if UIKit ever resumes and runs this to the end,
59+
// the animator stays .active (paused at 1.0) instead of going .inactive.
60+
// This guarantees didMoveToWindow can always call pauseAnimation() safely.
61+
newAnimator.pausesOnCompletion = true
62+
newAnimator.startAnimation()
63+
newAnimator.pauseAnimation()
64+
newAnimator.fractionComplete = intensity
65+
animator = newAnimator
5866
}
5967

6068
deinit {
61-
guard let animator = animator, animator.state == .active else { return }
62-
animator.stopAnimation(true)
63-
animator.finishAnimation(at: .current)
69+
if let animator = animator, animator.state == .active {
70+
animator.stopAnimation(true)
71+
}
6472
}
6573
}
6674

0 commit comments

Comments
 (0)