Skip to content

Instantly share code, notes, and snippets.

@cecilemuller
Created April 9, 2023 07:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cecilemuller/984ad715eb01c34678b622466037fa37 to your computer and use it in GitHub Desktop.
Save cecilemuller/984ad715eb01c34678b622466037fa37 to your computer and use it in GitHub Desktop.
Swift: distort a UIView to fit arbitrary corners
import UIKit
extension UIView {
/// Updates `anchorPoint`, `frame`, and `layer.transform` to fit arbitrary corners.
/// Based on https://stackoverflow.com/questions/9470493/transforming-a-rectangle-image-into-a-quadrilateral-using-a-catransform3d/39981054#39981054
func distort(topLeft tl: CGPoint, topRight tr: CGPoint, bottomLeft bl: CGPoint, bottomRight br: CGPoint) {
// Frame ----------------------------------------------------------- /
let xmin = min(tr.x, tl.x, bl.x, br.x)
let ymin = min(tr.y, tl.y, bl.y, br.y)
let xmax = max(tr.x, tl.x, bl.x, br.x)
let ymax = max(tr.y, tl.y, bl.y, br.y)
self.layer.anchorPoint = .zero
self.layer.transform = CATransform3DIdentity
self.frame = CGRect(
x: xmin,
y: ymin,
width: xmax - xmin,
height: ymax - ymin
)
// Transform ------------------------------------------------------- //
let rect = self.bounds
let XX = rect.origin.x
let YY = rect.origin.y
let WW = rect.size.width
let HH = rect.size.height
let x1a = tl.x - xmin
let y1a = tl.y - ymin
let x2a = tr.x - xmin
let y2a = tr.y - ymin
let x3a = bl.x - xmin
let y3a = bl.y - ymin
let x4a = br.x - xmin
let y4a = br.y - ymin
let y21 = y2a - y1a
let y32 = y3a - y2a
let y43 = y4a - y3a
let y14 = y1a - y4a
let y31 = y3a - y1a
let y42 = y4a - y2a
let a = -HH * (x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42)
let b = WW * (x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43)
let c0 = -HH * WW * x1a * (x4a*y32 - x3a*y42 + x2a*y43)
let cx = HH * XX * (x2a*x3a*y14 + x2a*x4a*y31 - x1a*x4a*y32 + x1a*x3a*y42)
let cy = -WW * YY * (x2a*x3a*y14 + x3a*x4a*y21 + x1a*x4a*y32 + x1a*x2a*y43)
let c = c0 + cx + cy
let d = HH * (-x4a*y21*y3a + x2a*y1a*y43 - x1a*y2a*y43 - x3a*y1a*y4a + x3a*y2a*y4a)
let e = WW * (x4a*y2a*y31 - x3a*y1a*y42 - x2a*y31*y4a + x1a*y3a*y42)
let f0 = -WW * HH * (x4a * y1a * y32 - x3a * y1a * y42 + x2a * y1a * y43)
let fx = HH * XX * (x4a * y21 * y3a - x2a * y1a * y43 - x3a * y21 * y4a + x1a * y2a * y43)
let fy = -WW * YY * (x4a * y2a * y31 - x3a * y1a * y42 - x2a * y31 * y4a + x1a * y3a * y42)
let f = f0 + fx + fy
let g = HH * (x3a * y21 - x4a * y21 + (-x1a + x2a) * y43)
let h = WW * (-x2a * y31 + x4a * y31 + (x1a - x3a) * y42)
let iy = WW * YY * (x2a * y31 - x4a * y31 - x1a * y42 + x3a * y42)
let ix = HH * XX * (x4a * y21 - x3a * y21 + x1a * y43 - x2a * y43)
let i0 = HH * WW * (x3a * y42 - x4a * y32 - x2a * y43)
var i = i0 + ix + iy
let kEpsilon: CGFloat = 0.0001
if abs(i) < kEpsilon {
i = kEpsilon * (i > 0 ? 1 : -1)
}
self.layer.transform = CATransform3D(
m11: a/i, m12: d/i, m13: 0, m14: g/i,
m21: b/i, m22: e/i, m23: 0, m24: h/i,
m31: 0, m32: 0, m33: 1, m34: 0,
m41: c/i, m42: f/i, m43: 0, m44: 1.0
)
}
/// Resets the transformation.
func undistort(frame: CGRect) {
self.layer.transform = CATransform3DIdentity
self.frame = frame
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment