2

I have a custom animator for view controller interactive transition. There is also a blur effect that is set to nil depending on the transition progress. The effect's animation code is the following:

@objc func blurEffectDissmisal() {

    UIView.animate(withDuration: dismissAnimator.durationOfAnimation + 1, animations: {
        self.blurEffectView?.effect = nil
    }) { (done) in
        if (done) {
       self.blurEffectView?.removeFromSuperview()
        }
    }
}

I call it by a notification, which is called on the second view controller when the transition from it to the first one starts.

However, I have a problem here. The completion block is called before the animation ends. When I run the transition for the first time (without canceling it) it works fine, but during the subsequents runs it doesn't.

I had also tried to add the animation to my animator but it didn't work out, either.

Moreover, the completion block gets called before the actual animation ends when I cancel the transition (in this case, I understand why but can't figure out how to make it move backwards. Maybe I should create a reverse animation in a completion block?)

I have tried the suggestion from this answer as you can see, but doesn't help.

If you know how this problem could be solved, I would really appreciate your help.

Tigran Iskandaryan
  • 1,371
  • 2
  • 14
  • 41
  • To clarify, the code runs as expected, but setting an instance to nil cannot be animated... – Dominik Bucher Feb 04 '18 at 14:11
  • I added answer, please check it and try out in playground, if it solves your problem please mark it as best answer. If you have any questions just ask below my anwer. – Dominik Bucher Feb 04 '18 at 14:14

3 Answers3

2

Use a delay before calling animate function .

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        UIView.animate(withDuration: 2.0,delay: 0, animations: {
            self.frame.origin.x = -2000
        }) { (done) in
            if(done){
                self.removeFromSuperview()
            }
            
        }
    }
Sateesh Pasala
  • 772
  • 8
  • 15
0
  UIView.animate(withDuration: 1, animations: {
                self.blurEffectView?.frame = CGRect(x: self.blurEffectView?.frame.origin.x, y: self.view.frame.size.height, width: self.blurEffectView?.frame.size.width, height: self.blurEffectView?.frame.size.height)
            }, completion: {
                (value: Bool) in
                self.blurEffectView?.removeFromSuperview()
            })  

Using the same animation (on UIView) I am able to achieve this :

enter image description here

Nitish
  • 13,845
  • 28
  • 135
  • 263
  • Sorry, but the effect is the same. Your first code is the same as mine (it just doesn't check for the completion of the animation in the closure), isn't it? – Tigran Iskandaryan Feb 04 '18 at 13:54
  • Did you try the second code as well ? Because I have been using it. It slides the view from its original position to bottom of screen and then the view is removed from superview – Nitish Feb 04 '18 at 13:56
  • What is the difference in your first code block and the code in question ignoring the fact the question uses constant as duration? – Dominik Bucher Feb 04 '18 at 14:02
  • Yeah. Honestly speaking my very first animation code was like that one, but I decided to change it (make blur just dissolve). The problem is, when I run the transition for the first time it works fine, but when I try again, in a half way my `blurEffectView` just disappears (gets removed from its superview via completion block) – Tigran Iskandaryan Feb 04 '18 at 14:03
  • Thank you for the answer. My `view` was behaving exactly like that, but only when I was running the animation for the first time. However, I have already solved that issue. It was because I had forgotten to remove `NotificationCenter`' s observer. Currently I'm working on solving the second issue of playing the animation back when the transition is canceled. – Tigran Iskandaryan Feb 04 '18 at 14:47
0

I've created a playground where you can take a look at this change, just create new playground, click on the assistant editor (top left corner, two joined circles) and take a look at the animation. This should do.

import PlaygroundSupport
import UIKit


let rect = CGRect(x: 0, y: 0, width: 40, height: 40)
let view = UIView(frame: rect )
view.backgroundColor = UIColor.yellow
let label = UILabel(frame: rect)
label.text = "A"
label.textAlignment = .center
view.addSubview(label)

let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = view.bounds
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.addSubview(blurEffectView)

PlaygroundPage.current.liveView = view

UIView.animate(withDuration: 4, animations: {
    blurEffectView.alpha = 0
}) { (done) in
    if (done) {
        blurEffectView.removeFromSuperview()
    }
}

The problem you are facing is not that the UIView.animate doesn't do it's job, it's just because you are setting the view to nil is not animable. Like imagine deleting someething could be animated...

Dominik Bucher
  • 2,120
  • 2
  • 16
  • 25
  • Yes, you are right. I don't want to animate the removal of the effect from superview, of course. The problem was that the completion block was called before the animation was done. However, I've figured out that I wasn't removing the `observer` from my `NotificationCenter`. However, I still have the problem of removing the effect from the superview when the transition is canceled. In that case, the animation should play back instead of removing the blur effect. – Tigran Iskandaryan Feb 04 '18 at 14:22
  • It's always better to use delegate or KVO rather than Notification center, things like this can happen... What you can do is set the userInteractionEnabled to false when the animation ends and when you want it back se it to true again to ignore the user taps... – Dominik Bucher Feb 04 '18 at 14:27
  • Thank you for your advice. I was using a delegate instead of a `Notification Center` but, then I thought that `Notification Center` is less verbose in case of lines of code, so I decided to use that technique. Anyway, thanks for suggestion. I have a table view in the first `view controller`, so when the blur effect is gone, user should be able to interact with it. But honestly speaking, I don't quite understand which view's `userInteractionEnabled` property you are talking about. Could you clarify that part? – Tigran Iskandaryan Feb 04 '18 at 14:34
  • Okay seems like I didn't get the question at first. I thought you may want to reuse the blur add/remove, so it would be maybe suitable (not memory efficient) to set `alpha` value of the blurView to 0 and `userInteractionEnabled` to false since when you set this, the touch recognizer goes to the next layer. – Dominik Bucher Feb 04 '18 at 14:37
  • Yes, in this case, setting the `alpha` value of the `view` to 0 wouldn't be memory efficient, because the first `viewController` is quite heavy (in case of a number of views), so it is better to remove `blurEffectView` from the hierarchy. Currently I'm trying to solve the view's removal from hierarchy when the transition is canceled. Maybe you have some suggestions about playing the `blurEffectView`'s animation back, because I can't figure out how to solve that problem, yet? – Tigran Iskandaryan Feb 04 '18 at 14:43