Created
April 9, 2023 07:43
-
-
Save cecilemuller/984ad715eb01c34678b622466037fa37 to your computer and use it in GitHub Desktop.
Swift: distort a UIView to fit arbitrary corners
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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