0

I have UIDynamicAnimator property defined:

lazy fileprivate var animator: UIDynamicAnimator = {
        return UIDynamicAnimator(referenceView: self)
}()

self is subclass from UIView;

In extension of self class, the same file, I have logic with animations, that uses my animator, adds UIDynamicBehavior items:

    let pushBehavior = UIPushBehavior(items: [stampView], mode: .continuous)
//some settings
    let dynamicItemBehavior = UIDynamicItemBehavior(items: [stampView])
//some settings
    let gravityBehavior = UIGravityBehavior(items: [stampView])
//some settings
    let collisionBehavior = UICollisionBehavior(items: [stampView])
//some settings

Everything works fine, but when I try to stop all animations with removeAllBehaviors() animations stop, but behaviors all still in animator.behaviors. The second time I call it, array becomes empty.

//======

For my pushBehavior I add action, which changes var, indicated, that I achieve destination point:

pushBehavior.action = { [unowned stampView] in
            if stampView.center.x <= endPosition.x {
                lastJump = true
            }
        }

In collisionBehavior delegate method I check this variable and try to stop animations with removeAllBehaviors()

public func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) {
    if lastJump {
        //animator.behaviors.count = 4
        animator.removeAllBehaviors()
        //still, animator.behaviors.count = 4
    }
}
Vladyslav Zavalykhatko
  • 15,202
  • 8
  • 65
  • 100

1 Answers1

1

You say you are testing like this:

public func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) {
    if lastJump {
        //animator.behaviors.count = 4
        animator.removeAllBehaviors()
        //still, animator.behaviors.count = 4
    }
}

Well, animator.removeAllBehaviors() is a command that the behaviors should be removed, but that command cannot be obeyed now, because those behaviors are still operating, including the one your code is right in the middle of. If the behaviors really stopped right at that instant, we would never even reach the next line of your code!

So the animator rightly does not in fact remove the behaviors until after your code has stopped running (also known as the end of the run loop).

The way to work around this to wait until after your code has stopped before calling removeAllBehaviors(). You can easily do that using my delay utility (https://stackoverflow.com/a/24318861/341994):

public func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) {
    if lastJump {
        delay(0.1) {
            animator.removeAllBehaviors()
        }
    }
}
Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • thank you for your answer, but they are not removed after few seconds too. I have some action (let it be button tap, doesn't matter actually) and after animation stopped, I press this button, behaviors are still there – Vladyslav Zavalykhatko Oct 19 '16 at 16:35
  • Okay, see my revised answer. But I think there is something more that you are not telling me. – matt Oct 19 '16 at 17:16
  • yes, it is the issue. I'm trying to remove behavior in method, called from behaviors delegate property. all animations stop, but behaviors seems to be retained. thanks! – Vladyslav Zavalykhatko Oct 20 '16 at 12:22