Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
242 views
in Technique[技术] by (71.8m points)

ios - calculate new size and location on a CGRect

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:

from

To:

to

question from:https://stackoverflow.com/questions/65952562/calculate-new-size-and-location-on-a-cgrect

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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...

  • if rect A was 100 x 100
  • and rect B was 200 x 200

The scaleFactor would be 200 / 100 == 2 ... that is, B is 2x bigger.

For your values:

  • rect A is 375 x 375
  • rect B is 1080 x 1080

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 A1 origin is x: 30 y: 20
    • rect A1 size is 50 x 25
  • rect B is 600 x 600

scaleFactor is 600 / 200 == 3, so

  • rect B1 origin is x: (30 * 3) y: (20 * 3)
  • rect B1 size is (50 * 3) x (25 * 3)

If that's all we've done so far, we would get this result:

enter image description here

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:

enter image description here

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
        
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...