Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determining if UIView in array of UIView's overlap with each other at all, and then adjust position

I have a loop below that is supposed to detect if a view in an array of UIView's overlap with each other at all. If they do, then adjust their center value.

Each view is 25x25.

func drawCircles() {
        for i in 0..<circles.count{
            circles[i].center = getRandomPoint()
            for j in 0..<circles.count{
                if(i != j) {
                    let comparingCentre = circles[j].center
                    let dist = distance(comparingCentre, circles[i].center)
                    if dist <= 25 {
                        
                        var newCenter = circles[i].center
                        var centersVector = CGVector(dx: newCenter.x - comparingCentre.x, dy: newCenter.y - comparingCentre.y)
                     
                        //Circle width is 25
                        centersVector.dx *= 26 / dist
                        centersVector.dy *= 26 / dist
                        newCenter.x = comparingCentre.x + centersVector.dx
                        newCenter.y = comparingCentre.y + centersVector.dy
                        circles[i].center = newCenter
                    }
                }
            }
        }
...
}

Below is the method for generating a random CGPoint that gets set to the view's center:

func getRandomPoint() -> CGPoint {
    let viewMidX = self.circlesView.bounds.midX
    let viewMidY = self.circlesView.bounds.midY
    
    let xPosition = self.circlesView.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
    let yPosition = self.circlesView.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
    let point = CGPoint(x: xPosition, y: yPosition)
    return point
}

And below is the method for determining the distance between two UIView's.

func distance(_ a: CGPoint, _ b: CGPoint) -> CGFloat {
    let xDist = a.x - b.x
    let yDist = a.y - b.y
    return CGFloat(hypot(xDist, yDist))
}

However, I'm still experiencing, occasionally, when two view's will overlap each other (see the red-circled section below):

enter image description here

EDIT This is code that adds the circles to the view:

func generateCircles() {
    numberOfCircles = Int.random(in: 1..<50)
    let circleWidth = CGFloat(25)
    let circleHeight = circleWidth
    
    var i = 0
    while i < numberOfCircles {
        let circleView = CircleView(frame: CGRect(x: 0.0, y: 0.0, width: circleWidth, height: circleHeight))
        let number = Int.random(in: 0..<2)
        if number == 1 {
            circleView.mainColor = .yellow
        } else {
            circleView.mainColor = .blue
        }
      
        circles.append(circleView)
        i += 1
    }
    drawCircles()
}
like image 361
narner Avatar asked Dec 01 '25 11:12

narner


2 Answers

Here's a quick option based on my comment above about moving the logic for comparing distance into creation of the random point: This can be cleaned up, but I didn't want to fully rewrite it and used as much of your code as possible.

In the generateCirles function I only changed how you init your CircleView

func generateCircles() {
    numberOfCircles = Int.random(in: 1..<50)
    
    let circleWidth = CGFloat(25)
    let circleHeight = circleWidth
    
    for _ in 0..<numberOfCircles {
        let circleView = CircleView(frame: CGRect(origin: getRandomPoint(), size: (CGSize(width: circleWidth, height: circleHeight))))
        let number = Int.random(in: 0..<2)
        if number == 1 {
            circleView.mainColor = .yellow
        } else {
            circleView.mainColor = .blue
        }
        
        circles.append(circleView)
    }
    drawCircles()
}

In the getRandomPoint I added a check to see if the new point is a valid point based on your existing distance logic:

func getRandomPoint() -> CGPoint {
    let viewMidX = self.circlesView.bounds.midX
    let viewMidY = self.circlesView.bounds.midY
    
    let xPosition = self.circlesView.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
    let yPosition = self.circlesView.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
    let point = CGPoint(x: xPosition, y: yPosition)
    
    if !validatePoint(point) {
        return getRandomPoint()
    }
    return point
}

func validatePoint(_ point: CGPoint) -> Bool {
    for circle in circles {
        if distance(circle.frame.origin, point) <= 25 {
            return false
        }
    }
    return true
}

And I removed all of your code from drawCircles that you posted.

Here's what it looks like with 300 circles: (I used the view frame as the circlesView frame, so that's why the circles are over lapping with the screen bounds) This shouldn't be an issue for you, as I assume your circlesView frame is set to be smaller. enter image description here

like image 121
valosip Avatar answered Dec 03 '25 23:12

valosip


I think you should be able to do something like this:

let firstCircle = circles[i]
let secondCircle = circles[j]
let intersect = firstCircle.frame.intersection(secondCircle.frame)

if intersect.isNull {
  // The circles do NOT overlap.
} else {
  // The circles overlap. Handle accordingly.
}

Note that this doesn't handle the various edge cases such as comparing the same circles with each other, but this should simplify understanding if they overlap.

like image 27
AdamPro13 Avatar answered Dec 04 '25 01:12

AdamPro13



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!