3

I am using CoreGraphics to have a CGLayerRef that I store into a variable.

I draw paths into the CGLayerRef offscreen and then when I need, I render it. This works great because I am drawing lots of paths and this helps with the performance.

The problem occurs when the iPad's orientation changes. When this happens I need to keep my my drawings in the CGLayerRef without any changes, despite the fact that the frame changed. I don't want to redraw everything inside of it, because it will be very slow.

So... how will I keep it as it was in the previous orientation?

To sum it up in pictures:

This is my normal portrait orientation: enter image description here

And when the orientation switches my goal is that it will look something like this: enter image description here

Thanks in advance,

-David

bobbypage
  • 2,157
  • 2
  • 21
  • 21
  • not clear for me - you say _I need to keep my drawings without any changes_, and then show **changed** image rotated on 90 degrees. – beryllium Feb 21 '12 at 07:30
  • No, I just need the drawings of the paths to stay constant in the same position (hence the drawing) even though the view's frame changes (from 768 by 1024 (portrait) to 1024 by 768 (landscape). By default the paths get moved to different positions upon rotation, I think this is a core graphics "feature", that I can't disable... – bobbypage Feb 21 '12 at 07:37
  • So, do you need to keep your Layer at the same position in both orientations? – beryllium Feb 21 '12 at 07:43
  • the layer and the paths that are drawn. – bobbypage Feb 21 '12 at 07:50
  • this question isn't clear enough. Please describe the problem behavior - how are the paths changed? Picture of the incorrect behavior you are seeing might help. And, of course, some code wouldn't hurt. – Dad Feb 26 '12 at 07:42
  • So do you have a root view with this layer *and* other content in it? And when someone rotates the iPad, do you want the other content to rotate but not this layer? If instead you want nothing to rotate, you can just disable autorotation. – Jordan Miner Feb 27 '12 at 22:26
  • Jordan, I can't disable rotation completely, because I have other UIView's I need to rotate (toolbar.) – bobbypage Feb 28 '12 at 00:41

2 Answers2

1

It sounds like you want a view that always draws its contents in the same orientation relative to the device, regardless of the orientation of the interface (status bar, etc.). So in your images, if “hello” is drawn with the “h” by the physical volume buttons, then you always want the “h” drawn by the physical volume buttons, even if the user has rotated the device (and the rest of the UI has rotated).

iOS rotates the user interface by setting the transform of your root view controller's view. You can check this in the debugger:

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

So you can undo this by applying the inverse transform to your graphics context before drawing your content. But you have to adjust the origin of your graphics context too, which makes it a bit trickier.

First, you need to move the origin to the center of the view. Then you apply the inverse transform to undo the interface rotation. Then you move the origin back. Assuming your CGLayer is the same size as your view (in portrait orientation), this should work:

- (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);
}

It's not perfect. It ends up drawing as I said: always in the same orientation relative to the device. However, because of the way interface rotation works, when the user rotates the device, it redraws the view rotated 90 degrees, and the rotates the view into the correct orientation.

I have put a test project here:

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

In the test project, you can see a translucent toolbar that acts normally - it rotates to the edge of the screen nearest the ground. Under that is the view that uses the drawRect: method above to compensate for the user interface rotation.

rob mayoff
  • 375,296
  • 67
  • 796
  • 848
  • rob, thanks for the advice. I tried your approach and it didn't work. I actually was trying to solve this issue using a similar approach without using rootViewController transform, but doing the following: – bobbypage Feb 22 '12 at 05:53
  • 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 Feb 22 '12 at 05:53
  • This almost works but the issue that I'm facing is that myLayer is drawn at CGPointZero is actually displayed around the center of the view, not where it should be. Do you have a idea on how to solve this? – bobbypage Feb 22 '12 at 05:54
  • Please edit your question and put your code there. Also I would need to see a picture of your “center of the view” problem. It would help if the background weren't white so I could see the extent of the view. – rob mayoff Feb 22 '12 at 05:58
  • rob, thanks so much for the sample project. I will take a look at it when I will be home... – bobbypage Feb 28 '12 at 19:31
  • rob, thanks for your help. I have tried your approach. It works, but when the paths are user drawn they can sometimes are sometimes moved a couple pixels (it is the nature of using CGContextTranslate). As a result, I implemented this solution: http://stackoverflow.com/questions/7353789/ios-disable-autorotation-for-a-subview – bobbypage Mar 02 '12 at 23:09
1

If you don't want the interface orientation to change then just comment out the shouldAutorotateToInterfaceOrientation method in your view controller.

claywebb
  • 61
  • 6
  • He already posted a comment saying he can't disable rotation because he has other views that need to rotate. – rob mayoff Feb 28 '12 at 04:30