Reputation: 1
I have a CGRect
(let's call it A1
) with know size and location that has a parent A
of know size. A1
has all sort of CGAffineTransform
applied to it.
I need to find the location and size of a second rectangle relative to a parent B
of known (but different dimensions) that would look proportional to A1
in relation to A
Basically a "zoom" effect. What would A1
dimensions and position be to look similar when placed in B
. Kind of a "scale the CGAffineTransform" ?
visual guide:
From:
To:
Upvotes: 0
Views: 1021
Reputation: 77452
Assuming you are transforming the rect like this:
scale it
move it
rotate it
First, you want to calculate the "scaleFactor" between the two sizes. So...
The scaleFactor would be 200 / 100 == 2 ... that is, B is 2x bigger.
For your values:
the scaleFactor is 1080 / 375 == 2.88
So, let's go with values that make the math easy (and obvious):
rect A is 200 x 200
rect B is 600 x 600
scaleFactor is 600 / 200 == 3, so
If that's all we've done so far, we would get this result:
Next, we need to move (translate) it, and scale the translation. If the original translation is:
CGAffineTransform(translationX: 30, y: 100)
the new translation is:
CGAffineTransform(translationX: 30 * scaleFactor, y: 100 * scaleFactor)
We don't need to scale the rotation, because, well, it's the same rotation.
So, after setting the original frame based on our origin and size calculations, then applying the same scale transform, plus the scaled translation transform, plus the same rotation transform, we get:
Here's example code I used to produce that:
class ViewController: UIViewController {
let viewA: UIView = {
let v = UIView()
v.backgroundColor = .systemYellow
return v
}()
let viewARedView: UIView = {
let v = UIView()
v.backgroundColor = .systemRed
return v
}()
let viewABlueView: UIView = {
let v = UIView()
v.backgroundColor = .systemBlue
return v
}()
let viewB: UIView = {
let v = UIView()
v.backgroundColor = .systemYellow
return v
}()
let viewBRedView: UIView = {
let v = UIView()
v.backgroundColor = .systemRed
return v
}()
let viewBBlueView: UIView = {
let v = UIView()
v.backgroundColor = .systemBlue
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.setNavigationBarHidden(true, animated: false)
view.addSubview(viewA)
view.addSubview(viewB)
viewA.frame = CGRect(x: 20, y: 60, width: 200, height: 200)
viewB.frame = CGRect(x: 20, y: viewA.frame.maxY + 20, width: 600, height: 600)
viewA.addSubview(viewARedView)
viewA.addSubview(viewABlueView)
let origRect: CGRect = CGRect(x: 30, y: 20, width: 50, height: 25)
// set frames of red and blue rect to the same rectangles
// so blue is on top of red
// we're going to transform the blue rect
viewARedView.frame = origRect
viewABlueView.frame = origRect
// scale values
let scX: CGFloat = 2.25
let scY: CGFloat = 1.75
// translate values
let trX: CGFloat = 30.0
let trY: CGFloat = 100.0
// rotation value
let rot: CGFloat = .pi * -0.15
var tx: CGAffineTransform = .identity
// scale it
tx = tx.concatenating(CGAffineTransform(scaleX: scX, y: scY))
// move it
tx = tx.concatenating(CGAffineTransform(translationX: trX, y: trY))
// rotate it
tx = tx.concatenating(CGAffineTransform(rotationAngle: rot))
// apply the transform
viewABlueView.transform = tx
viewB.addSubview(viewBRedView)
viewB.addSubview(viewBBlueView)
// get the scale factor...
// in this case,
// viewA width is 200
// viewB width is 700
// so viewB is 3.5 times bigger (700 / 200)
let scaleFactor: CGFloat = viewB.frame.width / viewA.frame.width
// make the original rect origin and size
// "scaleFactor bigger"
let newRect: CGRect = CGRect(x: origRect.origin.x * scaleFactor,
y: origRect.origin.y * scaleFactor,
width: origRect.width * scaleFactor,
height: origRect.height * scaleFactor)
// set frames of red and blue rect to the same rectangles
// so blue is on top of red
// we're going to transform the blue rect
viewBRedView.frame = newRect
viewBBlueView.frame = newRect
// scale the translation values
let trXb: CGFloat = trX * scaleFactor
let trYb: CGFloat = trY * scaleFactor
var txb: CGAffineTransform = .identity
// we've already changed the initial size of the rectangle,
// so use same scaling values
txb = txb.concatenating(CGAffineTransform(scaleX: scX, y: scY))
// we need to use scaled translation values
txb = txb.concatenating(CGAffineTransform(translationX: trXb, y: trYb))
// rotation doesn't change based on scale
txb = txb.concatenating(CGAffineTransform(rotationAngle: rot))
// apply the transform
viewBBlueView.transform = txb
}
}
Upvotes: 2