Detect Collision of Two UIview's in Swift

Detecting the collision b/w two objects in Swift 3.0?

// The Pan Gesture
func createPanGestureRecognizer(targetView: UIImageView)
{

var panGesture = UIPanGestureRecognizer(target: self, action:("handlePanGesture:"))
targetView.addGestureRecognizer(panGesture)
}

// THE HANDLE

func handlePanGesture(panGesture: UIPanGestureRecognizer) {

// get translation

var translation = panGesture.translationInView(view)
panGesture.setTranslation(CGPointZero, inView: view)
println(translation)

//create a new Label and give it the parameters of the old one
var label = panGesture.view as UIImageView
label.center = CGPoint(x: label.center.x+translation.x, y: label.center.y+translation.y)
label.multipleTouchEnabled = true
label.userInteractionEnabled = true

if panGesture.state == UIGestureRecognizerState.Began {

//add something you want to happen when the Label Panning has started
}

if panGesture.state == UIGestureRecognizerState.Ended {

//add something you want to happen when the Label Panning has ended

}

if panGesture.state == UIGestureRecognizerState.Changed {

//add something you want to happen when the Label Panning has been change ( during the moving/panning )

}

else {

// or something when its not moving
}

}

How to see if two UIImageView's have touched

You can check if UIImageViews are overlapping like that:

if img1.bounds.contains(img2.bounds) 
{
print("overlapped")
}

How to create a Collision Detection between a label and an Image

You want to use contains(). If you use intersects() then it will return true as soon as the two rectangles touch. With contains(), it won't return true until the dragged view is fully inside the target view, which seems much more intuitive.

I just wrote a sample app that implements this and it works perfectly.

I created a simple subclass of UIView I called BoxedView that just sets a border around it's layer so you can see it.

I set up a view controller with 2 boxed views, a larger "targetView", and a smaller view that the user could drag.

The target/action for my gesture recognizer moves the dragged view's frame as the user drags, and if target view contains the dragged view, it sets a Bool highlightTargetView, which causes the box around the target view to get thicker.

The entire view controller class' code looks like this:

class ViewController: UIViewController {

    @IBOutlet weak var targetView: BoxedView!

var viewStartingFrame: CGRect = CGRect.zero

var highlightTargetView: Bool = false {
didSet {
targetView.layer.borderWidth = highlightTargetView ? 5 : 1
}
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func userDraggedView(_ gesture: UIPanGestureRecognizer) {
switch gesture.state {
case .began:
viewStartingFrame = gesture.view?.frame ?? CGRect.zero
case .changed:
let offset = gesture.translation(in: view)
gesture.view?.frame = viewStartingFrame.offsetBy(dx: offset.x, dy: offset.y)
highlightTargetView = targetView.frame.contains(gesture.view?.frame ?? CGRect.zero)
case .ended:
gesture.view?.frame = viewStartingFrame
highlightTargetView = false
default:
break
}
}
}

In order for the math to work, I use the frame property of both views, which is in the coordinate system of the parent view (The parent view is the view controller's content view in this case, but the key thing is that we compare 2 rectangles using the same coordinate system for both.) If you used the bounds property of either view your math wouldn't work because bounds of a view is in the local coordinate system of that view.

Here's what that program looks like when running:

View dragging GIF

For comparison, I modified the program to also show what it looks like using the intersects() function, and created a video of what that looks like:

Sample Image

How to detect Collision between 2 UIViews

Right, so first you check for overlapping rectangles (which you've already done). When you find an overlap, then you need to refine the collision test. For each corner of the offending rectangle, compute the distance from the center of your character to the corner of the offending rectangle. If the distance from the center to any of the corners is less than the radius of the circle, then you have a collision. To optimize the code a little bit, compute (dx * dx + dy * dy) and compare that to the radius squared. That way you don't have to compute any square roots.

Upon further review, you also need to do an edge check in addition to the corner check. For example, if the top y value is above the center of the circle and the bottom y value is below the center of the circle, then compute the difference in x between the rectangle left edge and the center of the circle, and if that distance is less than the radius, then a collision has occurred. Likewise for the other three edges of the rectangle.

Here's some pseudo-code for the corner checking

int dx, dy, radius, radiusSquared;

radiusSquared = radius * radius;
for ( each rectangle that overlaps the player rectangle )
{
for ( each corner of the rectangle )
{
dx = corner.x - center.x;
dy = corner.y - center.y;
if ( dx * dx + dy * dy < radiusSquared )
Collision!!!
}
}

Detecting collisions between rotated UIViews

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.

UIImages or UIViews are not colliding each other in iOS Swift?

The issue is that the animator only recognizes the collision at the origin of the greenSquare. In this case it is at the top of the screen. You can update the collision location on the animator after you move the greenSquare. Add animator?.updateItem(usingCurrentState: greenSquare!)
to the end of the draggedView method. You do not need to use UISnapBehavior for this so you can remove the IBAction func pan method at the top of your code. The updateItem(usingCurrentState:) call will reset where the collision barrier is when you are moving the view manually and it is not being done by the physics engine.

How do I detect the collision of two SKShapeNodes in Swift without affecting their physics?

You have no contactTestBitMask on your sprites, so your didBegin is never called.

SwiftUI: How can I detect if two views are intersecting each other?

Your question is not stupid at all!

It seems to be easy

  1. read frame of first View (in global coordinates) and save it in preferences
  2. read frame of second View (in global coordinates) and save it in preferences
  3. calculate intersection when preferences changed

create our preference key structure is easy, it was already explained at this web site before (use search for more details :-))

struct Sizes: PreferenceKey {
typealias Value = [CGRect]
static var defaultValue: [CGRect] = []

static func reduce(value: inout [CGRect], nextValue: () -> [CGRect]) {
value.append(contentsOf: nextValue())
}
}

using GeometryReader in background View modifier was already explained

struct SizeReader: View {
var body: some View {
GeometryReader { proxy in
Color.clear
.preference(key: Sizes.self, value: [proxy.frame(in: .global)])
}
}
}

and now we can use it to save frame rectangles in our Sizes preference key

RectView(color: .pink).background(SizeReader())

What about our RectView ? To demonstrate how our "easy solution" works imagine that somebody create it with random size and random alignment

struct RectView: View {
let color: Color
let size = CGFloat(Int.random(in: 50 ... 200))
var body: some View {
color.frame(width: size, height: size)
.alignmentGuide(HorizontalAlignment.center) {
CGFloat.random(in: 0 ..< $0.width)
}
.alignmentGuide(VerticalAlignment.center) {
CGFloat.random(in: 0 ..< $0.width)
}
}
}

so far, so good, we are ready to check our "easy solution"

Lets finish our project with

struct ContentView: View {
@State var id = UUID()
var body: some View {
VStack {
ZStack {
Color.black
RectView(color: .pink)
.background(SizeReader())
RectView(color: .yellow)
.background(SizeReader())
.blendMode(.difference)
}
.onPreferenceChange(Sizes.self) { (value) in
let intersection = value[0].intersection(value[1])
print(value[0])
print(value[1])
print(intersection)
print()
}
.onTapGesture {
self.id = UUID()
}
Text("paceholder").id(id)
}
}
}

Run it and each time you click the black part of the screen you see two randomly sized and positioned rectangles and in the debug window printout of values ​​of our interest.

Sample Image

WARNING check the printout and you'll see that something if wrong!

The code is buggy example and I put it here to demonstrate, that your question is not stupid at all! There is a lot of thinks to understand about SwiftUI layout and how it works behind the scene.

I hope, somebody clever will try to explain what's wrong and will guide us on how to make it functional, all experts are welcome! Please don't change RectView, think that it is some build-in SwiftUI component



Related Topics



Leave a reply



Submit