计算 CGRect 上的新大小和位置

问题描述

我有一个 CGRect(我们称它为 A1),它的大小和位置都知道,它的父 A 的大小已知。 A1 应用了各种 CGAffineTransform

我需要找到相对于已知(但尺寸不同)的父 B 的第二个矩形的位置和大小,该父 A1 看起来与 A 成正比,相对于 A1

基本上是“缩放”效果。当放置在 B 中时,public class CustomTestExecutionListener implements TestExecutionListener,Ordered { public ByteArrayOutputStream request = new ByteArrayOutputStream(); public ByteArrayOutputStream response = new ByteArrayOutputStream(); public PrintStream requestvar = new PrintStream(request,true); public PrintStream responseVar = new PrintStream(response,true); public void beforeTestMethod(TestContext testContext) throws Exception { RestAssured.filters(new ResponseLoggingFilter(LogDetail.ALL,responseVar),new RequestLoggingFilter(LogDetail.ALL,requestvar)); } public void afterTestMethod(TestContext testContext) throws Exception { logRequest(request); logResponse(response); } @Override public int getorder() { return Integer.MAX_VALUE; } @Attachment(value = "Client RQ",type = "text/html") public byte[] logRequest(ByteArrayOutputStream stream) { return attach(stream); } @Attachment(value = "Client RS",type = "text/html") public byte[] logResponse(ByteArrayOutputStream stream) { return attach(stream); } public byte[] attach(ByteArrayOutputStream log) { byte[] array = log.toString().getBytes(StandardCharsets.UTF_8); log.reset(); return array; } } 的尺寸和位置看起来相似。有点像“缩放 CGAffineTransform”?

视觉指南:

来自:

from

致:

to

解决方法

假设你像这样转换矩形:

scale it
move it
rotate it

首先,您要计算两个尺寸之间的“scaleFactor”。所以...

  • 如果矩形 A 是 100 x 100
  • 和矩形 B 是 200 x 200

scaleFactor 将是 200 / 100 == 2 ...也就是说,B 是 2 倍大。

为了您的价值观:

  • 矩形 A 为 375 x 375
  • 矩形 B 为 1080 x 1080

比例因子是 1080 / 375 == 2.88

所以,让我们使用使数学变得简单(且显而易见)的值:

  • 矩形 A 为 200 x 200

    • 矩形 A1 原点是 x: 30 y: 20
    • 矩形 A1 尺寸为 50 x 25
  • 矩形 B 为 600 x 600

scaleFactor 是 600 / 200 == 3,所以

  • 矩形 B1 原点是 x: (30 * 3) y: (20 * 3)
  • 矩形 B1 大小为 (50 * 3) x (25 * 3)

如果这就是我们迄今为止所做的一切,我们会得到这样的结果:

enter image description here

接下来,我们需要移动(平移)它,并缩放平移。如果原文是:

CGAffineTransform(translationX: 30,y: 100)

新的翻译是:

CGAffineTransform(translationX: 30 * scaleFactor,y: 100 * scaleFactor)

我们不需要缩放旋转,因为它是相同的旋转。

因此,根据我们的原点和大小计算设置原始框架后,然后应用相同的缩放变换,加上缩放的平移变换,加上相同的旋转变换,我们得到:

enter image description here

这是我用来生成它的示例代码:

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