如何在设备方向切换后保持CGLayerRef常数?

3
我使用CoreGraphics创建CGLayerRef并将其存储到变量中。我在屏幕外绘制路径,然后在需要时将其呈现。这很有效率,因为我要画很多路径。
但是当iPad的方向改变时,会出现问题。此时,尽管框架发生了变化,我仍需要保持我的绘画不变。我不想重新绘制里面的所有东西,因为这样会非常慢。
那么...我该如何使它在以前的方向上保持不变?
简而言之:
这是我的正常肖像方向: enter image description here 当方向切换时,我的目标是让它看起来像这样: enter image description here 谢谢,David

对我来说不太清楚 - 你说“我需要保持我的图纸没有任何更改”,然后展示旋转了90度的更改后的图像。 - beryllium
不,我只需要路径的绘制位置保持不变(因此需要绘图),即使视图的框架发生变化(从768x1024(纵向)到1024x768(横向))。默认情况下,路径在旋转时会移动到不同的位置,我认为这是核心图形的“特性”,我无法禁用... - bobbypage
所以,您需要在两个方向上保持图层的相同位置吗? - beryllium
绘制的图层和路径。 - bobbypage
这个问题不够清晰。请描述问题的行为 - 路径是如何改变的?提供一张显示错误行为的图片可能会有所帮助。当然,一些代码也不会有害。 - Dad
显示剩余2条评论
2个回答

1

看起来你想要一个视图,它始终以相对于设备的相同方向绘制其内容,而不管界面(状态栏等)的方向如何。因此,在您的图像中,如果“hello”是由物理音量按钮旁边的“h”绘制的,则无论用户是否旋转了设备(以及UI的其余部分是否旋转),您始终希望“h”被物理音量按钮旁边的“h”绘制。

iOS通过设置根视图控制器的视图的变换来旋转用户界面。您可以在调试器中检查此操作:

# Unrotated
(lldb) po [[(id)UIApp keyWindow] recursiveDescription]
(id) $1 = 0x0b535030 <UIWindow: 0xd434a80; frame = (0 0; 320 480); layer = <UIWindowLayer: 0xd434b70>>
   | <UIView: 0xb5385e0; frame = (0 20; 320 460); autoresize = W+H; layer = <CALayer: 0xb538710>>

# Rotated with home button at the left
(lldb) po [[(id)UIApp keyWindow] recursiveDescription]
(id) $2 = 0x0f036060 <UIWindow: 0xd434a80; frame = (0 0; 320 480); layer = <UIWindowLayer: 0xd434b70>>
   | <UIView: 0xb5385e0; frame = (20 0; 300 480); transform = [0, -1, 1, 0, 0, 0]; autoresize = W+H; layer = <CALayer: 0xb538710>>

因此,在绘制内容之前,可以通过将逆变换应用于图形上下文来撤消此操作。但是,您还必须调整图形上下文的原点,这使得它有点棘手。

首先,您需要将原点移动到视图的中心。然后,应用逆变换以撤消界面旋转。然后,将原点移回。假设您的CGLayer与您的视图(在纵向方向上)具有相同的大小,则应该可以解决此问题:

- (void)drawRect:(CGRect)rect
{
    if (!_cgLayer) {
        [self initCGLayer];
    }

    CGContextRef gc = UIGraphicsGetCurrentContext();
    CGContextSaveGState(gc); {
        CGSize mySize = self.bounds.size;
        CGSize cgLayerSize = CGLayerGetSize(_cgLayer);
        CGContextTranslateCTM(gc, mySize.width / 2, mySize.height / 2);
        CGContextConcatCTM(gc, CGAffineTransformInvert(self.window.rootViewController.view.transform));
        CGContextTranslateCTM(gc, -cgLayerSize.width / 2, -cgLayerSize.height / 2);
        CGContextDrawLayerAtPoint(gc, CGPointZero, _cgLayer);
    } CGContextRestoreGState(gc);
}

这并不完美。它最终会像我说的那样绘制:始终相对于设备保持相同的方向。然而,由于界面旋转的工作方式,当用户旋转设备时,它会重新绘制视图并将其旋转90度,然后将视图旋转到正确的方向。

我在这里放了一个测试项目:

https://github.com/mayoff/stackoverflow-cglayer-unrotating

在测试项目中,您可以看到一个半透明的工具栏,它表现得很正常 - 它会旋转到最靠近地面的屏幕边缘。在其下方是视图,使用上面的drawRect:方法来补偿用户界面旋转。

Rob,感谢你的建议。我尝试了你的方法,但它没有起作用。实际上,我一直在尝试使用类似的方法解决这个问题,而不是使用rootViewController transform,但是采取了以下措施: - bobbypage
CGContextSaveGState(context); { if (needsRotation) { CGContextTranslateCTM(context, self.center.x, self.center.y); CGContextRotateCTM (context, degreesToRadians(-90)); needsRotation = NO; }CGContextDrawLayerAtPoint (context, CGPointZero, myLayer);} CGContextRestoreGState(context); - bobbypage
这个代码几乎就能实现,但我遇到的问题是 myLayer 在 CGPointZero 处绘制时,实际上显示在视图的中心,而不是应该显示的位置。您有解决方法吗? - bobbypage
请编辑您的问题并将代码放在那里。此外,我需要看到您“视图中心”的问题的图片。如果背景不是白色,那会更好,这样我就可以看到视图的范围。 - rob mayoff
Rob,非常感谢您提供的示例项目。我会在回家后查看它... - bobbypage
Rob,感谢你的帮助。我尝试了你的方法。它有效,但是当路径由用户绘制时,它们有时会移动几个像素(这是使用CGContextTranslate的本质)。因此,我实施了这个解决方案:https://dev59.com/72s05IYBdhLWcg3wQvke - bobbypage

1

如果您不希望界面方向发生变化,那么只需在视图控制器中注释掉 shouldAutorotateToInterfaceOrientation 方法即可。


他已经发表了评论,说他不能禁用旋转,因为他有其他需要旋转的视图。 - rob mayoff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接