如何围绕其中心旋转 CGRect?

How to rotate a CGRect about its centre?

背景:

我想显示顺时针旋转 90 度的标签。旋转的标签应该在我指定的某个框架中。比方说 (10, 100, 50, 100).

我知道我可以使用 CGAffineTransform 旋转视图。我也知道根据 docs.

转换完成后我不应该设置 frame 属性

When the value of this property is anything other than the identity transform, the value in the frame property is undefined and should be ignored.

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

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

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

Translate(x, y) * Rotate(θ) * Translate(-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)
}

由于我只想将视图旋转 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)