1

My app has a custom UIButton with two subviews that look like they're hanging by strings. When the user taps the button, one gets pulled back, then collides with the other. This is working for me on the first collision, but when I go to tap a second time, the item doesn't get pulled back.

On the first call to layoutSubviews(), I add both views to a UIGravityBehavior, and I give them each their own UIAttachmentBehavior. On touchesBegan, I add a collision adjacent to the view that needs to get pulled back, and then either on touchesEnded or after a timer if it was a tap and not a long press, I remove the collision that was holding it in place.

I store that collision in an instance variable (so it's retained), and create it like so:

    holdCollision = UICollisionBehavior(items: [theView])
    holdCollision.addBoundaryWithIdentifier("boundary suspending item in air",
        forPath: UIBezierPath(rect: holdCollisionViewFrame))

If I construct the UICollisionBehavior once and then call addBehavior and removeBehavior with it multiple times, it only works the first time. If I construct it from scratch on each tap, it always works. What could be causing this?

Update: I came up with a sample project for reporting to Apple. You can reproduce in a clean single-view iOS project. Add two views, one over the other, and link to the outlets at the top. The top view needs to be large enough that it overlaps with the bottom one at first launch, or you wont' see the problem. Add two buttons and connect to the actions at the bottom.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var topView: UIImageView!
    @IBOutlet weak var bottomView: UIView!

    var animator: UIDynamicAnimator!
    var collision: UICollisionBehavior!

    override func viewDidLoad() {
        super.viewDidLoad()

        animator = UIDynamicAnimator(referenceView: view)

        let gravity = UIGravityBehavior(items: [topView])
        animator.addBehavior(gravity)

        var anchor = view.center
        anchor.y -= 100

        let attachment = UIAttachmentBehavior(item: topView, attachedToAnchor: anchor)
        animator.addBehavior(attachment)

        collision = UICollisionBehavior(items: [topView])
        collision.addBoundaryWithIdentifier("bottom boundary", forPath: UIBezierPath(rect: bottomView.frame))
    }

    @IBAction func addCollision() {
        NSLog("boundaries: \(collision.boundaryIdentifiers)")
        animator.addBehavior(collision)
    }

    @IBAction func removeCollision() {
        animator.removeBehavior(collision)
    }
}
Dov
  • 15,530
  • 13
  • 76
  • 177
  • What is it colliding with? Wouldn't it make more sense to use a push behavior or force behavior? Are you sure it's not getting released? – beyowulf Nov 18 '15 at 01:07
  • @beyowulf I have it colliding with a boundary rather than an item. I tried a continuous push behavior, but it was causing it to oscillate, instead of holding steady. I updated the question to mention that I'm storing the collision behavior as an instance variable, so it's definitely not getting released. But when I logged it out, to verify, I noticed that it no longer has any boundaries after I remove it from the animator. I'll be reporting it as a bug to Apple. – Dov Nov 18 '15 at 14:01

1 Answers1

0

It looks like after I remove the holdCollision from the UIDynamicAnimator, it loses its list of boundaries. That seems like a bug, and I reported it as such. It's rdar://23593048 if anyone would like to dupe.

Dov
  • 15,530
  • 13
  • 76
  • 177