Skip to content

Instantly share code, notes, and snippets.

@rcholic
Forked from calosth/UIView+Intersection.swift
Created September 8, 2022 22:59
Show Gist options
  • Select an option

  • Save rcholic/ce036f9eb9e958facf99608d9076c7ba to your computer and use it in GitHub Desktop.

Select an option

Save rcholic/ce036f9eb9e958facf99608d9076c7ba to your computer and use it in GitHub Desktop.
Extension of UIView to detect collisions between them, including rotated(transformed) views.
extension UIView {
/// Given detects collision between the receiver and a given view
/// - Parameter view2: the view to compare with
public func intersectsWith(_ view2: UIView) -> Bool {
let polygonA = convertToPolygon(self)
let polygonB = convertToPolygon(view2)
return intersects(polygonA: polygonA, polygonB: polygonB)
}
private struct Polygon {
var points: [CGPoint] = []
/// Create a 2D rectangle polygon
init(vertexA: CGPoint, vertexB: CGPoint, vertexC: CGPoint, vertexD: CGPoint) {
points.append(vertexA)
points.append(vertexB)
points.append(vertexC)
points.append(vertexD)
}
}
private func projectionOf(_ polygon: Polygon, with normal: CGPoint) -> (minP: CGFloat, maxP: CGFloat) {
var minProjection: CGFloat = .infinity
var maxProjection: CGFloat = -.infinity
for point in polygon.points {
let projection: CGFloat = point.x * normal.x + point.y * normal.y
if projection > maxProjection {
maxProjection = projection
}
if projection < minProjection {
minProjection = projection
}
}
return (minProjection, maxProjection)
}
private func intersects(polygonA: Polygon, polygonB: Polygon) -> Bool {
for polygon in [polygonA, polygonB] {
for index in 0..<polygon.points.count {
let nextIndex = (index + 1) % polygon.points.count
let point1 = polygon.points[index]
let point2 = polygon.points[nextIndex]
let normal = CGPoint(x: -(point2.y - point1.y), y: point2.x - point1.x)
let (minProjectionA, maxProjectionA) = projectionOf(polygonA, with: normal)
let (minProjectionB, maxProjectionB) = projectionOf(polygonB, with: normal)
if maxProjectionA < minProjectionB || maxProjectionB < minProjectionA {
return false
}
}
}
return true
}
private func convertToPolygon(_ view: UIView) -> Polygon {
let transform = view.transform
var point: CGPoint = CGPoint(x: view.bounds.midX, y: view.bounds.midY)
point.x = -point.x
point.y = -point.y
var tL = __CGPointApplyAffineTransform(point, transform)
tL.x += view.center.x
tL.y += view.center.y
point.x = -point.x;
var tR = __CGPointApplyAffineTransform(point, transform)
tR.x += view.center.x
tR.y += view.center.y
point.y = -point.y;
var bR = __CGPointApplyAffineTransform(point, transform)
bR.x += view.center.x
bR.y += view.center.y
point.x = -point.x;
var bL = __CGPointApplyAffineTransform(point, transform)
bL.x += view.center.x
bL.y += view.center.y
let polygon = Polygon(vertexA: tL,
vertexB: tR,
vertexC: bR,
vertexD: bL)
return polygon
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment