1

我在我的iOS应用程序中有UIToolbar,我使用CGAffineTransform以编程方式在willRotateToInterfaceOrientation函数中旋转。转换需要缩放,旋转和平移。我知道如何计算比例和旋转参数,但似乎无法弄清楚如何计算翻译参数,以便视图位于正确的位置。经过大量的试验和错误之后,我确定了将视图移动到iPad上的正确位置的确切数字,但我不希望对数字进行硬编码,以便代码不会太依赖于设备。我如何计算翻译参数?如何在iOS中旋转视图时计算仿射转换参数?

这是我目前在包含工具栏的视图控制器中的willRotateToInterfaceOrientation函数。我想知道如何计算txty而不是硬编码值。我尝试过使用工具栏和窗口大小的各种功能,但工具栏始终显示为与窗口完全重叠在一个陌生的位置或完全位于窗口之外,而不是在窗口的底部。

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) 
    { 
     // First, apply identify transformation to simplify later calculations and also because we need the toolbar's frame and Apple's docs say you cannot use a view's frame if the transform property is not the identity matrix. 
     self.toolbar.transform = CGAffineTransformIdentity; 

     // Calculate affine parameters. 

     // Expand width to the window's height when in landscape mode, leaving the toolbar's height unchanged. 
     UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 
     CGFloat sx = 1.0; 
     CGFloat sy = window.frame.size.height/window.frame.size.width; 

     // Rotate 90 degrees in either direction depending on the orientation direction change. 
     CGFloat rotate = M_PI_2; 
     if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) 
     { 
      rotate = -M_PI_2; 
     } 

     // Reposition the toolbar. 
     // TODO: Calculate values rather than hard-coding them. Why is tx not something like ((window.frame.size.width/2) - (self.toolbar.frame.size.height/2))? 
     // Note that these values work for both the original iPad and iPad Retina, presumably because the values represent points not pixels. 
     CGFloat tx = -365.5; 
     CGFloat ty = 359.5; 
     if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) 
     { 
      tx = -tx; 
     } 

     // Apply affine transformation. 
     CGAffineTransform transform = CGAffineTransformMakeScale(sx, sy); 
     transform = CGAffineTransformRotate(transform, rotate); 
     transform = CGAffineTransformTranslate(transform, tx, ty); 
     [UIView animateWithDuration:duration 
         animations:^{ 
           self.toolbar.transform = transform; 
          } 
         completion:NULL 
     ]; 
    } 
    else if (toInterfaceOrientation == UIInterfaceOrientationPortrait) 
    { 
     [UIView animateWithDuration:duration 
         animations:^{ 
          self.toolbar.transform = CGAffineTransformIdentity; 
         }completion:NULL 
     ]; 
    } 
    else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) 
    { 
     NSLog(@"Upside-down orientation not supported"); 
    } 
} 

此外,这里是我的工具栏的声明和基本初始化。请注意,我将工具栏作为子视图添加到主窗口并手动处理旋转,因为我的应用程序具有作为根视图控制器的UITabBarController,这是我可以让工具栏出现在标签栏顶部的唯一方法。

// The toolbar is strong since this controller must maintain ownership as the toolbar is removed from the parent view when this view disappears. 
@property (strong, nonatomic) IBOutlet UIToolbar *toolbar; 

UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 

CGFloat height = CGRectGetHeight(self.tabBarController.tabBar.frame); // Completely cover the tab bar. 
CGFloat x = window.frame.origin.x; 
CGFloat y = window.frame.size.height - height; 
CGFloat width = window.frame.size.width; 

self.toolbar.frame = CGRectMake(x, y, width, height); 

[window addSubview:self.toolbar]; 

最后,我使用的Xcode 4.5.1启用ARC和我在iPad上(视网膜和非视网膜)5.1和6.0模拟器都测试。

+0

感谢您的回复。我的理解是,通过将给定的参数应用于现有的仿射变换,非Make函数(CGAffineTransformTranslate而不是CGAffineTransformMakeTranslate)构造一个新的变换矩阵。 – user19480 2013-03-25 01:28:04

+0

@ user19480你完全正确,我的错误,我错过了它不是Make类型的事实。道歉。 – WDUK 2013-03-25 01:40:15

回答

2

将我的工具栏的锚点从(.5,.5)的默认值设置为(0,0)后,我能够解决此问题。

// The toolbar is strong since this controller must maintain ownership as the toolbar is removed from the parent view when this view disappears. 
@property (strong, nonatomic) IBOutlet UIToolbar *toolbar; 

UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 

// Set anchor point to the top-left corner instead of to the center (.5, .5) so that affine transformations during orientation changes are easier to calculate. 
self.toolbar.layer.anchorPoint = CGPointMake(0, 0); 

CGFloat height = CGRectGetHeight(self.tabBarController.tabBar.frame); // Completely cover the tab bar. 
CGFloat x = window.frame.origin.x; 
CGFloat y = window.frame.size.height - height; 
CGFloat width = window.frame.size.width; 

self.toolbar.frame = CGRectMake(x, y, width, height); 

[window addSubview:self.toolbar]; 

下面是更正后的代码,其中转换参数txty作为工具栏和窗口大小的函数来计算,而不是硬编码。我用新的willRotateView:toInterfaceOrientation:duration:函数封装了逻辑,以便重用和旋转任意视图。 willRotateToInterfaceOrientation:duration:只是将旋转请求转发到此函数,并使用我的工具栏作为视图参数。

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
    [self willRotateView:self.toolbar toInterfaceOrientation:toInterfaceOrientation duration:duration]; 
} 

- (void)willRotateView:(UIView *)view toInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{ 
    if (UIInterfaceOrientationIsLandscape(toInterfaceOrientation)) 
    { 
     // First, apply identify transformation to simplify later calculations and also because we need the view's frame and Apple's docs say you cannot use a view's frame if the transform property is not the identity matrix. 
     view.transform = CGAffineTransformIdentity; 

     // Calculate affine parameters. 

     // Get the window's post-orientation change dimensions. 
     UIWindow *window = [[UIApplication sharedApplication] keyWindow]; 
     CGFloat rotatedWinWidth = window.frame.size.height; 
     CGFloat rotatedWinHeight = window.frame.size.width; 

     // Expand width to the window's height when in landscape mode, leaving the view's height unchanged. The scaling is done along the Y axis since the window's origin will change such that the Y axis will still run along the longer direction of the device. 
     CGFloat sx = 1.0; 
     CGFloat sy = rotatedWinWidth/rotatedWinHeight; 

     // Rotate 90 degrees in either direction depending on the orientation direction change. 
     CGFloat angle = M_PI_2; 
     if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) 
     { 
      angle = -M_PI_2; 
     } 

     // Reposition the view, assuming that view.layer.anchorPoint is (0, 0). 
     // Note that the height of the view is used as the X offset as this corresponds to the X direction since the rotated window's origin will also rotate. Also, the position has to take into account the width scale factor. 
     CGFloat xOffset = view.frame.size.height; 
     CGFloat tx = -(rotatedWinWidth - xOffset)/sy; 
     CGFloat ty = -view.frame.size.height; 
     if (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) 
     { 
      tx = -xOffset/sy; 
      ty = rotatedWinHeight - view.frame.size.height; 
     } 

     // Apply affine transformation. 
     CGAffineTransform transform = CGAffineTransformMakeScale(sx, sy); 
     transform = CGAffineTransformRotate(transform, angle); 
     transform = CGAffineTransformTranslate(transform, tx, ty); 
     [UIView animateWithDuration:duration 
         animations:^{ 
          view.transform = transform; 
         } 
         completion:NULL 
     ]; 
    } 
    else if (toInterfaceOrientation == UIInterfaceOrientationPortrait) 
    { 
     [UIView animateWithDuration:duration 
         animations:^{ 
          view.transform = CGAffineTransformIdentity; 
         }completion:NULL 
     ]; 
    } 
    else if (toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) 
    { 
     DLog(@"Upside-down orientation not supported"); 
    } 
} 
0

来自UIView类的引用:transform - 指定应用于接收者的变换,相对于其边界的中心。这意味着您的CGAffineTransform围绕框的中心旋转,而不是框架原点。您需要相对于其旋转位移来抵消原点。这些数字是边界框尺寸的函数。

看到这里使用范围的例子:iPhone correct landscape window coordinates

2

随着CGAffineTransform你需要在你做的事情,(大卫提到)的顺序非常小心,你需要知道你正在旋转的什么点或大小约。人们常常对旋转的结果感到惊讶,无论anchorPoint(你用旋转关系旋转的点)。

如果我们有这个为我们的出发观点:

original view

你可以有anchorPoint是在(0,0),或在图像的中心。要么可以工作。独自一人都不会给你想要的东西。

下面是从0,0:

rotated by 90

如果你只是想旋转视图来显示它,如果你有0,0锚点,你旋转和奇迹“发生了什么事我的看法!!!”但是你可以把它翻译成你想要的地方。

这里是在图像的中心设置的锚点:

rotated about center

可以看到所得到的视图,但其关断垂直和水平,因为它的一个矩形和边是不同长度。不过,您可以轻松地调整它,方法是将x移动原始高度差,然后将y移动原始宽度差。

+0

感谢您提供的信息和非常有帮助的图表。是的,我需要更改我的视图的锚点,这使我可以计算出所需的翻译值。 – user19480 2013-03-26 22:16:08

+0

不客气。 – HalR 2013-03-26 23:44:59