How do i make a circular view collide with the pad

2019-06-26 11:21发布

问题:

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.

回答1:

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.



回答2:

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
 }