Whenever the circular view collides with the paddle on storyboard, it actually collides with the rectangular frame of the view. How do I resolve this issue. I have also tried UIBezierPath
, CAShapeLayer
and it doesn't seem to be working.
The red arrow is the colliding point, As it does not seem to be colliding with ball instead it collides with the frame of the view. once, the frame is touched with the paddle it bounces back and so on.
If you're using UIDynamicAnimator
, you can define the collisionBoundsType
and the collisionBoundingPath
for the items that will be colliding, for example:
@IBDesignable public class BallView : UIView {
@IBInspectable public var fillColor: UIColor = UIColor.blackColor() {
didSet { setNeedsLayout() }
}
override public var collisionBoundsType: UIDynamicItemCollisionBoundsType {
return .Path
}
override public var collisionBoundingPath: UIBezierPath {
let radius = min(bounds.size.width, bounds.size.height) / 2.0
return UIBezierPath(arcCenter: CGPointZero, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI * 2.0), clockwise: true)
}
private var shapeLayer: CAShapeLayer!
public override func layoutSubviews() {
if shapeLayer == nil {
shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.clearColor().CGColor
layer.addSublayer(shapeLayer)
}
let radius = min(bounds.size.width, bounds.size.height) / 2.0
shapeLayer.fillColor = fillColor.CGColor
shapeLayer.path = UIBezierPath(arcCenter: CGPoint(x: bounds.size.width / 2.0, y: bounds.size.height / 2.0), radius: radius, startAngle: 0, endAngle: CGFloat(M_PI * 2.0), clockwise: true).CGPath
}
}
@IBDesignable public class PaddleView : UIView {
@IBInspectable public var cornerRadius: CGFloat = 5 {
didSet { setNeedsLayout() }
}
@IBInspectable public var fillColor: UIColor = UIColor.blackColor() {
didSet { setNeedsLayout() }
}
override public var collisionBoundsType: UIDynamicItemCollisionBoundsType {
return .Path
}
override public var collisionBoundingPath: UIBezierPath {
return UIBezierPath(roundedRect: CGRect(x: -bounds.size.width / 2.0, y: -bounds.size.height / 2.0, width: bounds.width, height: bounds.height), cornerRadius: cornerRadius)
}
private var shapeLayer: CAShapeLayer!
public override func layoutSubviews() {
if shapeLayer == nil {
shapeLayer = CAShapeLayer()
shapeLayer.strokeColor = UIColor.clearColor().CGColor
layer.addSublayer(shapeLayer)
}
shapeLayer.fillColor = fillColor.CGColor
shapeLayer.path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: bounds.width, height: bounds.height), cornerRadius: cornerRadius).CGPath
}
}
If you do this, and then add these items to your UICollisionBehavior
, it will respect the collision boundaries you defined for the items. For example:
The above illustrates a glancing blow, like in your original image. If you want it to bounce up even if it hits the edge of the paddle, you can specify the collisionDelegate
for the UICollisionBehavior
and give it a new direction:
let collision = UICollisionBehavior(items: [paddleView, ballView])
collision.translatesReferenceBoundsIntoBoundary = true
collision.collisionDelegate = self
self.animator.addBehavior(collision)
And conform to UICollisionBehaviorDelegate
and then implement collisionBehavior(beganContactForItem:withItem:atPoint:)
:
func collisionBehavior(behavior: UICollisionBehavior, beganContactForItem item1: UIDynamicItem, withItem item2: UIDynamicItem, atPoint p: CGPoint) {
let postCollisionDirection = UIDynamicItemBehavior(items: [ballView])
postCollisionDirection.addLinearVelocity(CGPoint(x: ballView.center.x - paddleView.center.x, y: -200), forItem: ballView)
animator.addBehavior(postCollisionDirection)
}
That yields something like:
Clearly, you'll have to play around with this quite a bit to get the desired effect, but it illustrates the basic idea of detecting a collision and adding a linear velocity to the ball, accordingly.
An even simpler answer than the accepted answer would be to set the bounds to be elliptical instead of a defined path:
override public var collisionBoundsType: UIDynamicItemCollisionBoundsType {
return .Elliptical
}