3

I have two UIViews, one of which is rotated every .01 second using the following code:

    self.rectView.transform = CGAffineTransformRotate(self.rectView.transform, .05);

Now, I want to be able to tell if another UIView, called view, intersects rectView. I used this code:

if(CGRectIntersectsRect(self.rectView.frame, view.frame)) {
    //Intersection
}

There is a problem with this, however, as you probably know. Here is a screenshot: enter image description here

In this case, a collision is detected, even though obviously they are not touching. I have looked around, but I cannot seem to find real code to detect this collision. How can this be done? Working code for detecting the collision in this case would be greatly appreciated! Or maybe would there be a better class to be using other than UIView?

7
  • I don't know the answer but the cause is probably down to the fact that a view's frame is not affected by any transforms applied to a view. Commented Feb 13, 2014 at 15:25
  • Ok @Jasarien, do you know of a better class I could be using other than UIView? Commented Feb 13, 2014 at 15:26
  • I think you should use some OpenGL framework if you plan to do a lot of things like it. If it's casual, you can still do some arithmetics. But this kind are already done in most of game framework. Commented Feb 13, 2014 at 15:37
  • If your application is for iOS 7 and more, check the UICollisionBehavior class. Commented Feb 13, 2014 at 15:55
  • If you're intending to make a game, I would recommend using a game framework, like Sprite Kit or Cocos 2d. While it's still possible make games with UIKit, you'll find it a lot easier to do with a game framework that will handle things like collision for you, built into the framework. Commented Feb 13, 2014 at 16:00

2 Answers 2

2

when you rotate a view, its bounds won't change but its frame changes.

So, for my view with backgroundColor blue,
the initial frame i set to was

frame = (30, 150, 150, 35);
bounds={{0, 0}, {150, 35}};

but after rotating by 45 degree, the frame changed to

frame = (39.5926 102.093; 130.815 130.815);
bounds={{0, 0}, {150, 35}};

screenshot of running app showing frame of blue view with black border

Because the frame always return the smallest enclosing rectangle of that view.

So, in your case, even-though it looks both views are not intersecting,their frames intersect.

To solve it you can use, separating axis test. If you want learn on it, link here

I tried to solve it and finally got the solution. If you like to check, below is the code. Copy paste the below code into an empty project to check it out.

In .m file

@implementation ViewController{
    UIView *nonRotatedView;
    UIView *rotatedView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    nonRotatedView =[[UIView alloc] initWithFrame:CGRectMake(120, 80, 150, 40)];
    nonRotatedView.backgroundColor =[UIColor blackColor];
    [self.view addSubview:nonRotatedView];

    rotatedView =[[UIView alloc] initWithFrame:CGRectMake(30, 150, 150, 35)];
    rotatedView.backgroundColor =[UIColor blueColor];
    [self.view addSubview:rotatedView];
    CGAffineTransform t=CGAffineTransformMakeRotation(M_PI_4);
    rotatedView.transform=t;

    CAShapeLayer *layer =[CAShapeLayer layer];
    [layer setFrame:rotatedView.frame];
    [self.view.layer addSublayer:layer];
    [layer setBorderColor:[UIColor blackColor].CGColor];
    [layer setBorderWidth:1];

    CGPoint p=CGPointMake(rotatedView.bounds.size.width/2, rotatedView.bounds.size.height/2);

    p.x = -p.x;p.y=-p.y;
    CGPoint tL =CGPointApplyAffineTransform(p, t);
    tL.x +=rotatedView.center.x;
    tL.y +=rotatedView.center.y;

    p.x = -p.x;
    CGPoint tR =CGPointApplyAffineTransform(p, t);
    tR.x +=rotatedView.center.x;
    tR.y +=rotatedView.center.y;

    p.y=-p.y;
    CGPoint bR =CGPointApplyAffineTransform(p, t);
    bR.x +=rotatedView.center.x;
    bR.y +=rotatedView.center.y;

    p.x = -p.x;
    CGPoint bL =CGPointApplyAffineTransform(p, t);
    bL.x +=rotatedView.center.x;
    bL.y +=rotatedView.center.y;


    //check for edges of nonRotated Rect's edges
    BOOL contains=YES;
    CGFloat value=nonRotatedView.frame.origin.x;
    if(tL.x<value && tR.x<value && bR.x<value && bL.x<value)
        contains=NO;
    value=nonRotatedView.frame.origin.y;
    if(tL.y<value && tR.y<value && bR.y<value && bL.y<value)
        contains=NO;
    value=nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width;
    if(tL.x>value && tR.x>value && bR.x>value && bL.x>value)
        contains=NO;
    value=nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height;
    if(tL.y>value && tR.y>value && bR.y>value && bL.y>value)
        contains=NO;

    if(contains==NO){
        NSLog(@"no intersection 1");
        return;
    }
    //check for roatedView's edges
    CGPoint rotatedVertexArray[]={tL,tR,bR,bL,tL,tR};

    CGPoint nonRotatedVertexArray[4];
    nonRotatedVertexArray[0]=CGPointMake(nonRotatedView.frame.origin.x,nonRotatedView.frame.origin.y);
    nonRotatedVertexArray[1]=CGPointMake(nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width,nonRotatedView.frame.origin.y);
    nonRotatedVertexArray[2]=CGPointMake(nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width,nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height);
    nonRotatedVertexArray[3]=CGPointMake(nonRotatedView.frame.origin.x,nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height);

    NSInteger i,j;
    for (i=0; i<4; i++) {
        CGPoint first=rotatedVertexArray[i];
        CGPoint second=rotatedVertexArray[i+1];
        CGPoint third=rotatedVertexArray[i+2];
        CGPoint mainVector =CGPointMake(second.x-first.x, second.y-first.y);
        CGPoint selfVector =CGPointMake(third.x-first.x, third.y-first.y);
        BOOL sign;
        sign=[self crossProductOf:mainVector withPoint:selfVector];
        for (j=0; j<4; j++) {
            CGPoint otherPoint=nonRotatedVertexArray[j];
            CGPoint otherVector = CGPointMake(otherPoint.x-first.x, otherPoint.y-first.y);
            BOOL checkSign=[self crossProductOf:mainVector withPoint:otherVector];
            if(checkSign==sign)
                break;
            else if (j==3)
                contains=NO;
        }
        if(contains==NO){
            NSLog(@"no intersection 2");
            return;
        }
    }
    NSLog(@"intersection");
}


-(BOOL)crossProductOf:(CGPoint)point1 withPoint:(CGPoint)point2{
    if((point1.x*point2.y-point1.y*point2.x)>=0)
         return YES;
    else
        return NO;
}

Hope this helps.

Sign up to request clarification or add additional context in comments.

1 Comment

Wow. More than I was possibly hoping for. I would up vote this a million times if I could. THANK YOU SO MUCH @santhu!
0

This can be done much more efficiently and easily than what has been suggested... and both the black and blue views can be rotated if need be.

Just convert the 4 corners of the rotated blueView to their location in the superview and then convert those points to their location in the rotated blackView then check if those points are within the blackView's bounds.

UIView *superview = blueView.superview;//Assuming blueView.superview and blackView.superview are the same...

CGPoint blueView_topLeft_inSuperview = [blueView convertPoint:CGPointMake(0, 0) toView:superview];
CGPoint blueView_topRight_inSuperview = [blueView convertPoint:CGPointMake(blueView.bounds.size.width, 0) toView:superview];
CGPoint blueView_bottomLeft_inSuperview = [blueView convertPoint:CGPointMake(0, blueView.bounds.size.height) toView:superview];
CGPoint blueView_bottomRight_inSuperview = [blueView convertPoint:CGPointMake(blueView.bounds.size.width, blueView.bounds.size.height) toView:superview];

CGPoint blueView_topLeft_inBlackView = [superview convertPoint:blueView_topLeft_inSuperview toView:blackView];
CGPoint blueView_topRight_inBlackView = [superview convertPoint:blueView_topRight_inSuperview toView:blackView];
CGPoint blueView_bottomLeft_inBlackView = [superview convertPoint:blueView_bottomLeft_inSuperview toView:blackView];
CGPoint blueView_bottomRight_inBlackView = [superview convertPoint:blueView_bottomRight_inSuperview toView:blackView];

BOOL collision = (CGRectContainsPoint(blackView.bounds, blueView_topLeft_inBlackView) ||
                  CGRectContainsPoint(blackView.bounds, blueView_topRight_inBlackView) ||
                  CGRectContainsPoint(blackView.bounds, blueView_bottomLeft_inBlackView) ||
                  CGRectContainsPoint(blackView.bounds, blueView_bottomRight_inBlackView));

2 Comments

Hmm, isn't there a possibility of there being a collision, but none of the corners of the blue rectangle are within the black rectangle? Like if the blue rectangle is at a 45 degree angle and the corner of the black rectangle is touching the side. I guess we might have to do the same process with the corners of the black rectangle, checking if they're within the blue rectangle as well?
@SamuelNoyes The above code should work for all corners of both rects automatically, regardless of eithers' rotation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.