【问题标题】:How to rotate a CGRect about its centre?如何围绕其中心旋转 CGRect?
【发布时间】:2019-03-02 08:58:13
【问题描述】:

背景:

我想显示一个顺时针旋转 90 度的标签。旋转的标签应该在我指定的某个框架中。假设是 (10, 100, 50, 100)。

我知道我可以使用CGAffineTransform 旋转视图。根据docs,我也知道在转换完成后我不应该设置frame属性。

当该属性的值不是恒等变换时,frame 属性中的值是未定义的,应该被忽略。

我尝试在转换后设置框架,虽然它有效,但我不想做文档告诉我不要做的事情。

label2.transform = CGAffineTransform(rotationAngle: -.pi / 2)
label2.frame =  CGRect(x: 10, y: 100, width: 50, height: 100)

然后我想,我可以这样做:

  1. 创建我想要的框架的CGRect
  2. 将该矩形逆时针旋转 90 度
  3. 将该矩形设置为标签的框架
  4. 将标签顺时针旋转 90 度

然后标签将在所需的框架中。

所以我尝试了这样的事情:

    let desiredFrame = CGRect(x: 10, y: 100, width: 50, height: 100) // step 1
    // I am using UIViews here because I am just looking at their frames
    // whether it is a UIView or UILabel does not matter
    // label1 is a view that is in the desired frame but not rotated
    // If label2 has the correct frame, it should completely cover label1
    let label1 = UIView(frame: desiredFrame)
    let label2Frame = rotateRect(label1.frame) // step 2
    let label2 = UIView(frame: label2Frame) // step 3
    view.addSubview(label1)
    view.addSubview(label2)
    label1.backgroundColor = .red
    label2.backgroundColor = .blue
    label2.transform = CGAffineTransform(rotationAngle: -.pi / 2) // step 4

rotateRect 声明如下:

func rotateRect(_ rect: CGRect) -> CGRect {
    return rect.applying(CGAffineTransform(rotationAngle: .pi / 2))
}

这不起作用。 label2 根本不重叠 label1。我什至在屏幕上都看不到label2。

我怀疑这是因为CGRect 中的applying 方法围绕原点旋转矩形,而不是矩形的中心。所以我尝试首先将矩形转换为原点,旋转它,然后将其转换回来,正如 math.SE 上的 post 所说:

func rotateRect(_ rect: CGRect) -> CGRect {
    let x = rect.x
    let y = rect.y
    let transform = CGAffineTransform(translationX: -x, y: -y)
                        .rotated(by: .pi / 2)
                        .translatedBy(x: x, y: y)
    return rect.applying(transform)
}

但是,我仍然无法在屏幕上看到label2。

【问题讨论】:

    标签: ios swift cgaffinetransform cgrect


    【解决方案1】:

    我认为您的转换顺序不正确。如果你这样做,那,

    平移(x, y) * 旋转(θ) * 平移(-x, -y)

    将它与您的rotateRect 一起使用似乎可以正常工作。由于您将视图旋转了 90 度,因此蓝色视图完全挡住了红色视图。换个角度试试,效果会更明显。

    func rotateRect(_ rect: CGRect) -> CGRect {
        let x = rect.midX
        let y = rect.midY
        let transform = CGAffineTransform(translationX: x, y: y)
                                        .rotated(by: .pi / 2)
                                        .translatedBy(x: -x, y: -y)
        return rect.applying(transform)
    }
    

    【讨论】:

    • 这很奇怪,因为如果您在 (x, y) 有一个点,并且您希望它回到原点,您不应该对 (-x, -y) 进行转换吗?我的理解哪里出错了?
    • 我明白了,原来是矩阵乘法的顺序!代码读作“先做A,再做B”,但实际上是做C,B,然后是A,这确实令人困惑。我实际上找到了一个不需要这些转换的解决方案。
    • 是的,它似乎是相反的顺序。它首先应用 origintransaltion 然后对其应用旋转,然后应用反向平移。
    【解决方案2】:

    由于我只想将视图旋转 90 度,初始帧的宽度和高度将与旋转帧的宽度和高度相反。

    初始帧的中心和旋转后的中心是相同的,所以我们可以在变换前将标签的center设置为所需帧的中心。

        let desiredFrame = CGRect(x: 10, y: 100, width: 50, height: 100)
        let label1 = UIView(frame: desiredFrame)
        // get the centre of the desired frame
        // this is also equal to label1.center
        let center = CGPoint(x: desiredFrame.midX, y: desiredFrame.midY)
        let label2Frame = CGRect(x: 0, y: 0, width: desiredFrame.height, height: desiredFrame.width)
        let label2 = UIView(frame: label2Frame)
        view.addSubview(label1)
        view.addSubview(label2)
        label1.backgroundColor = .red
        label2.backgroundColor = .blue
        label2.center = center // set the centre of label2 before the transform
        label2.transform = CGAffineTransform(rotationAngle: -.pi / 2)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-18
      相关资源
      最近更新 更多