确定自定义iOS视图是否重叠

问题描述

我已经定义了一个CircleView类:

class CircleView: UIView {
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = UIColor.clear
    }
        
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func draw(_ rect: CGRect) {
        // Get the Graphics Context
        if let context = UIGraphicsGetCurrentContext() {
            
            // Set the circle outerline-width
            context.setlinewidth(5.0);
            
            // Set the circle outerline-colour
            UIColor.blue.set()
            
            // Create Circle
            let center = CGPoint(x: frame.size.width/2,y: frame.size.height/2)
            let radius = (frame.size.width - 10)/2
            context.addArc(center: center,radius: radius,startAngle: 0.0,endAngle: .pi * 2.0,clockwise: true)
                        
            context.setFillColor(UIColor.blue.cgColor)
                
            // Draw
            context.strokePath()
            context.fillPath()
        }
    }

}

并使用随机设置的数字创建了一个数组:

    var numberOfCircles: Int!
    var circles: [CircleView] = []

    numberOfCircles = Int.random(in: 1..<10)
    let circleWidth = CGFloat(50)
    let circleHeight = circleWidth
    
    var i = 0
    while i < numberOfCircles {
        let circleView = CircleView(frame: CGRect(x: 0.0,y: 0.0,width: circleWidth,height: circleHeight))
        circles.append(circleView)
        i += 1
    }

创建圆后,我调用一个函数drawCircles,它将在屏幕上绘制它们:

 func drawCircles(){
        
        for c in circles {
            c.frame.origin = c.frame.randomPoint
            while !UIScreen.main.bounds.contains(c.frame.origin) {
                                    c.frame.origin = CGPoint()
                                    c.frame.origin = c.frame.randomPoint
 
                let prev = circles.before(c)
                if prev?.frame.intersects(c.frame) == true {
                    c.frame.origin = c.frame.randomPoint
                }
            }
        }
                         
        for c in circles {
            self.view.addSubview(c)
        }
    }

while方法中的drawCircles循环可确保没有任何圆放置在屏幕边界之外,并且可以按预期工作。

我正在努力确保彼此之间不重叠,就像这样:

enter image description here

enter image description here

我正在使用以下方法来确定下一个

我正在使用这种方法来确定圈子数组中的上一个/下一个元素:

extension BidirectionalCollection where Iterator.Element: Equatable {
    typealias Element = Self.Iterator.Element

    func after(_ item: Element,loop: Bool = false) -> Element? {
        if let itemIndex = self.firstIndex(of: item) {
            let lastItem: Bool = (index(after:itemIndex) == endindex)
            if loop && lastItem {
                return self.first
            } else if lastItem {
                return nil
            } else {
                return self[index(after:itemIndex)]
            }
        }
        return nil
    }

    func before(_ item: Element,loop: Bool = false) -> Element? {
        if let itemIndex = self.firstIndex(of: item) {
            let firstItem: Bool = (itemIndex == startIndex)
            if loop && firstItem {
                return self.last
            } else if firstItem {
                return nil
            } else {
                return self[index(before:itemIndex)]
            }
        }
        return nil
    }
}

这是if语句;似乎没有按照我的意愿去做;这是为了确保如果一个圆与另一个圆相交,请将其原点更改为新的东西:

if prev?.frame.intersects(c.frame) == true {
    c.frame.origin = c.frame.randomPoint
}

如果任何人对逻辑可能存在的任何想法,或关于如何确保圆圈彼此不重叠的其他想法,那将是有帮助的!

编辑:我确实尝试了Eugene这样回答的建议,但仍然得到了相同的结果:

  func distance(_ a: CGPoint,_ b: CGPoint) -> CGFloat {
        let xdist = a.x - b.x
        let ydist = a.y - b.y
        return CGFloat(sqrt(xdist * xdist + ydist * ydist))
    }
    

       if prev != nil {
                
                if distance((prev?.frame.origin)!,c.frame.origin) <= 40 {
                    print("2")
                    c.frame.origin = CGPoint()
                    c.frame.origin = c.frame.randomPoint
                    
                }
            }

但结果仍然相同

编辑2

根据Eugene编辑的答案/说明修改了我的for循环;仍然有圆圈重叠的问题:

对于圈子中的c { c.frame.origin = c.frame.randomPoint

    let prev = circles.before(c)
    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)))

    
    if let prev = prev {
        if distance(prev.center,c.center) <= 50 {
            c.center = CGPoint(x: xPosition,y: yPosition)
        }
    }

}

解决方法

这纯粹是几何挑战。只要确保圆心之间的距离大于或等于其半径之和即可。

编辑1

使用UIView.center代替UIView.frame.originUIView.frame.origin为您提供UIView的左上角。

if let prev = prev {
    if distance(prev.center,c.center) <= 50 {
         print("2")
         c.center = ...
    }
}

编辑3

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

let prev = circles.before(c)

if let prevCircleCenter = prev?.center {
    let distance = distance(prevCenter,c.center)
    if distance <= 50 {
        let viewMidX = c.bounds.midX
        let viewMidY = c.bounds.midY
      
        var newCenter = c.center
        var centersVector = CGVector(dx: newCenter.x - prevCircleCenter.x,dy: newCenter.y - prevCircleCenter.y)
        centersVector.dx *= 51 / distance
        centersVector.dy *= 51 / distance
        newCenter.x = prevCircleCenter.x + centersVector.dx
        newCenter.y = prevCircleCenter.y + centersVector.dy
        c.center = newCenter
    }
}