@@ -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