I worked on an app and implemented collision detection using UIKitDynamics.
Collision detection is working. But somehow the UICollisionBehaviorDelegate methods are not called. No warnings or errors are shown.
I created an example project to demonstrate the issue:
import UIKit
class ViewController: UIViewController {
private var dynamicAnimator: UIDynamicAnimator?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.initPhysics()
}
fileprivate var initalized = false
fileprivate func initPhysics() {
if initalized {
return
}
initalized = true
let physicsView = UIView(frame: view.bounds.insetBy(dx: 40, dy: 40))
physicsView.backgroundColor = UIColor.black
self.dynamicAnimator = UIDynamicAnimator(referenceView: physicsView)
physicsView.isUserInteractionEnabled = false
view.addSubview(physicsView)
//Create ball
let frame = CGRect(x: 0, y: 0, width: 40, height: 40)
let rect = UIView(frame: frame)
rect.backgroundColor = UIColor.red
physicsView.addSubview(rect)
let behavior = UIDynamicItemBehavior(items: [rect])
behavior.elasticity = 1.0
behavior.resistance = 0.0
behavior.friction = 0.0
behavior.allowsRotation = false
self.dynamicAnimator?.addBehavior(behavior)
//collision behavior setup
let collisionBehavior = UICollisionBehavior(items: [rect])
collisionBehavior.collisionDelegate = self
collisionBehavior.setTranslatesReferenceBoundsIntoBoundary(with: .zero)
collisionBehavior.collisionMode = .everything
self.dynamicAnimator?.addBehavior(collisionBehavior)
//Push ball
let pushBehavior = UIPushBehavior(items: [rect], mode: UIPushBehaviorMode.instantaneous)
pushBehavior.active = true
pushBehavior.pushDirection = CGVector(dx: 0.5, dy: 0.5)
self.dynamicAnimator?.addBehavior(pushBehavior)
}
}
extension ViewController: UICollisionBehaviorDelegate {
// DELEGATE METHODS NOT CALLED
func collisionBehavior(_ behavior: UICollisionBehavior,
beganContactFor item: UIDynamicItem,
withBoundaryIdentifier identifier: NSCopying?,
at p: CGPoint) {
print("began contact boundary")
}
func collisionBehavior(_ behavior: UICollisionBehavior,
endedContactFor item: UIDynamicItem,
withBoundaryIdentifier identifier: NSCopying?) {
print("contact boundary ended")
}
func collisionBehavior(_ behavior: UICollisionBehavior,
endedContactFor item1: UIDynamicItem,
with item2: UIDynamicItem) {
print("contact item ended")
}
func collisionBehavior(_ behavior: UICollisionBehavior,
beganContactFor item1: UIDynamicItem,
with item2: UIDynamicItem,
at p: CGPoint) {
print("began contact item")
}
}
Base SDK: iOS 11.2, Xcode Version 9.2 (9C40b)
Thank you for your help!
That's not how it works (sadly).. You have only ONE item in your collision behaviour and you are asking it to detect collision with what??? If you added a second item, you'll see the delegate get called..
The real question is why? Well, collision detects if the frame of one item intersects with the frame of another.. If it did what you are asking (
rect
is a child ofphysicsView
), then it will ALWAYS be colliding (which is not what you want)..Since your
physicsView
isn't part of the collision behaviour, you get nothing.. You told it to limit the item within its bounds (setTranslatesReferenceBoundsIntoBoundary
works on the items added to the behaviour itself), but not to detect collision with the bounds. For example, if the item was outside thephysicsView
, it wouldn't be able to get in. If it's inside, it can't get out. But it won't be counted as a collision per say (otherwise it is always colliding).In order to detect collision between your
rect
and yourphysicsView
, you need to givephysicsView
some physical properties.. Right now it's just a regularUIView
(reference view)..One other way to work around having to give it physical properties is to add a boundary for it:
Now it will call: