Victor Engel
Victor Engel

Reputation: 2133

CGAffineTransformMakeRotation of subview

Here's the scenario. I have a main view whose orientation I want to align with true north.

Inside this view I have 7 subviews arranged in a heptagonal pattern around the center of the main view. Each of these sub views is essentially the same as the others, so drawing relative to their local coordinates makes the most sense.

I thought I would use CGAffineTransformMakeRotation to rotate the subviews by integer multiples of 2*M_PI/7, in order to arrange them properly. So far so good.

I also thought I would use CGAffineTransformMakeRotation to rotate the main view according to compass values. To test that, so far I've just been hard coding values. That's fine, too.

The problem is, that I'm getting behavior that is different from what I expected. I thought the rotation of the subview would be relative to the main view, but it seems to be relative to the window containing the main view instead. Did I have a wrong expectation? Will I need to compose the two transformations for the subview rotations? That's fine if I have to do that, but it differs from what I expected, so maybe I'm doing something wrong.

Here is the code to rotate the main view. I currently have it as the last thing in drawRect for the m file for the main view.

      float angleFromNorth = 2*43.0 / M_PI;
  //      angleFromNorth = 0.;
  CGAffineTransform transform = CGAffineTransformMakeRotation(angleFromNorth);
  [self setTransform:transform];

Here is the code to rotate each of the subviews. This code is in the drawRect method for the subview. The subviews are created in the m file for the main view.

      float angle = 2 * M_PI * self.tag / self.NumPlatforms;
  CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
  [self setTransform:transform];

    (lldb) po [[UIApp keyWindow] recursiveDescription]
(id) $1 = 0x06c78ff0 <UIWindow: 0x6c80370; frame = (0 0; 768 1024); layer = <UIWindowLayer: 0x6c80450>>
   | <CircusView: 0x6c807a0; frame = (0 0; 768 1024); layer = <CALayer: 0x6c80a40>>
   | <PlatformView: 0x8880cd0; frame = (234 362; 300 300); layer = <CALayer: 0x887d310>>
   | <PlatformView: 0x6c805f0; frame = (173.202 301.202; 421.596 421.596); transform = [0.62349, 0.781832, -0.781832, 0.62349, 0, 0]; tag = 1; layer = <CALayer: 0x6c80820>>
   | <PlatformView: 0x6c81870; frame = (204.383 332.383; 359.235 359.235); transform = [-0.222521, 0.974928, -0.974928, -0.222521, 0, 0]; tag = 2; layer = <CALayer: 0x6c80630>>
   | <PlatformView: 0x8884710; frame = (183.772 311.772; 400.456 400.456); transform = [-0.900969, 0.433884, -0.433884, -0.900969, 0, 0]; tag = 3; layer = <CALayer: 0x887cd80>>
   | <PlatformView: 0x6a91a60; frame = (183.772 311.772; 400.456 400.456); transform = [-0.900969, -0.433884, 0.433884, -0.900969, 0, 0]; tag = 4; layer = <CALayer: 0x6a8a200>>
   | <PlatformView: 0x688f970; frame = (204.383 332.383; 359.235 359.235); transform = [-0.222521, -0.974928, 0.974928, -0.222521, 0, 0]; tag = 5; layer = <CALayer: 0x688a710>>
   | <PlatformView: 0x8884750; frame = (173.202 301.202; 421.596 421.596); transform = [0.62349, -0.781832, 0.781832, 0.62349, 0, 0]; tag = 6; layer = <CALayer: 0x887e290>>
(lldb) 

P.S. Now I've run into another hurdle. I thought having resolved the above scenario, the next step would be straightforward, but it is turning out to be a challenge. You may wish to follow my link in the comment section to see what the model looks like as rendered by paper/pencil and/or Google Sketchup. Anyway, the next step is to put towers on the platforms. I've created a new subclass UIView called Tower, with the intent to put three towers on each platform. The tricky part is that the orientation needs to match true north, i.e., circusView orientation, but the placement needs to align with the platform.

To do this, I created the subviews (of self.window) in the platform implementation file. I thought by doing it within this file that the coordinates would be relative to this view. And they probably are, before the rotation, come to think of it. But they need to keep the location relative to the rotated platforms but orient aligned with true north. The result will be that rectangles drawn this way on each platform should be placed on the platform the same way for each platform, but the rectangle should be rotated relative to the platform, i.e., not at all relative to the circusView (don't ask me why it's called circus -- left over from converting from another project).

Any suggestions? I'm thinking now I'll have to just initialize the towers the same way I finally got the platforms to work, and then rotate them by the inverse of the platform rotation.

Upvotes: 0

Views: 1968

Answers (1)

rob mayoff
rob mayoff

Reputation: 386038

Don't set the subviews' transforms in drawRect:. Do it in layoutSubviews on the parent.

static CGFloat radiansForDegrees(CGFloat degrees) {
    return degrees * 2 * M_PI / 360;
}

- (void)layoutSubviews {
    self.transform = CGAffineTransformMakeRotation(radiansForDegrees(43));

    CGFloat count = self.platformViews.count;
    for (UIView *platformView in self.platformViews) {
        platformView.transform = CGAffineTransformMakeRotation(platformView.tag * 2 * M_PI / count);
    }
}

You can trigger layoutSubviews by sending setNeedsLayout to the view, but you probably don't need to. The system will also automatically schedule a call to layoutSubviews at other times, such as when the view's size changes or when it gains or loses a subview.

Upvotes: 2

Related Questions