Mihail Minkov
Mihail Minkov

Reputation: 2633

Swift blur effect repeating on view change

I am using a blurViewEffect using the following code inside the viewDidLoad() function. I am using Storyboard and Swift in the controller of the specific screen to achieve this.

let blurEffectView = UIVisualEffectView()
blurEffectView.backgroundColor = .clear
blurEffectView.frame = contenedorPaywall.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contenedorPaywall.addSubview(blurEffectView)

blurAnimator = UIViewPropertyAnimator(duration: 0.5, curve: .linear) { [blurEffectView] in
    blurEffectView.effect = UIBlurEffect(style: .light)
}

It works perfectly for the structure I have, but the problem is that as soon as I change sections and come back it repeats the effect and everything gets blurrier.

Example 1 First Blur

Normal blur

Example 2 After switching to a different section/page and then switching back to the same screen

enter image description here

As you can see the second image is way blurrier. What I have noticed is that it seems to stop there. If I keep switching it stays the same level of blur as the second image, but I want to use the first one so more is visible below the text.

I tried re-running the blur code from zero, but I am not 100% sure how it works in this case. From what I understand it's more like an animation/transition.

How can I stop it from blurring more?

UPDATE on cookednick's answer

I changed the code to this because I wasn't sure where the startAnimation should go, but now it does the full blur instead. What am I doing wrong?

let blurEffectView = UIVisualEffectView()
blurEffectView.backgroundColor = .clear
blurEffectView.frame = contenedorPaywall.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
contenedorPaywall.addSubview(blurEffectView)

blurAnimator = UIViewPropertyAnimator(duration: 0.185, curve: .linear) { [blurEffectView] in
    blurEffectView.effect = UIBlurEffect(style: .light)
}
        
blurAnimator.startAnimation()
Timer.scheduledTimer(withTimeInterval: 0.185, repeats: false) { [blurAnimator] _ in
    if blurAnimator!.isRunning {
        blurAnimator!.stopAnimation(true)
        blurAnimator!.fractionComplete = 0.185
    }
}

Upvotes: 0

Views: 314

Answers (2)

userMJ
userMJ

Reputation: 13

I had the same issue but wanted to avoid adding a timer because it felt unreliable to me. But using the same "timing" idea, I simply stopped and finished the animator on the main queue after the initial fractionComplete has been added.

func setUpBlurView() {
    if (blurEffectView == nil) {
        blurEffectView = UIVisualEffectView()
        blurEffectView.backgroundColor = .clear

        blurEffectView.frame = CGRect(
            x: 0,
            y: 0,
            width: UIScreen.main.bounds.size.width,
            height: UIScreen.main.bounds.size.height
        )
        presenter.view.addSubview(blurEffectView)
        
        blurAnimator = UIViewPropertyAnimator(duration: 3, curve: .linear) {
            [blurEffectView] in
            blurEffectView?.effect = UIBlurEffect(style: .light)
        }
        blurAnimator.fractionComplete = 0.15

        presenter.view.bringSubviewToFront(self.view)

        DispatchQueue.main.async {
            self.blurAnimator?.stopAnimation(true)

            if let blurAnimator = self.blurAnimator, blurAnimator.state != .inactive {
                blurAnimator.finishAnimation(at: .current)
            }
        }
    }
}

Upvotes: 0

cookednick
cookednick

Reputation: 1088

What this looks like to me is a case where Example 1 only exists because the UIViewPropertyAnimator fails to actually finish the first time it is run. You'll have to look at any code that starts/stops it. Use print statements or breakpoints to learn if they are called at times you do not expect. You can print the progress value of the animation, and if it's less than 1.0 when Example 1 happens, there's your answer.

Example 2 is the correct, full effect. This is also why you don't see it blur even further. This is because the progress of the animation has already reached 1.0 (100%) and cannot go further.

Edit:

If you would like to use the look of Example 1 and never the look of 2, you can do that with a timer.

When creating the animator:

// Use a duration of 1, double the intended length.
blurAnimator = UIViewPropertyAnimator(duration: 1, curve: .linear) { [blurEffectView] in
    blurEffectView.effect = UIBlurEffect(style: .light)
}

When starting the animation:

blurAnimator.startAnimation()
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { [blurAnimator] _ in
    // After 0.5 seconds have passed, we will manually stop the animator if it's still running.
    if blurAnimator.isRunning {
        blurAnimator.stopAnimation(true)
        blurAnimator.fractionComplete = 0.5
    }
}

Upvotes: 0

Related Questions