1

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

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];

最后,我使用启用了 ARC 的 Xcode 4.5.1,并在 iPad(Retina 和非 Retina)5.1 和 6.0 模拟器上进行测试。

4

3 回答 3

2

使用 CGAffineTransform,您需要非常小心地执行操作,并且(正如 David 提到的)您需要知道您正在旋转或调整大小的位置。人们常常对旋转的结果感到惊讶,而不管锚点(您使用仿射旋转的点)。

如果我们有这个作为我们的起始视图:

原始视图

您可以让 anchorPoint 位于 (0, 0) 或图像的中心。任何一个都可以工作。两者都不可能单独给您想要的东西。

这是从 0 到 0:

旋转 90

如果您只是想旋转视图以显示它,如果您有 0、0 锚点,您会旋转并想知道“我的视图发生了什么!!!” 但是你可以把它翻译到你想要的地方。

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

绕中心旋转

您可以看到结果视图,但它在垂直和水平方向上都关闭了,因为它是一个矩形并且边长不同。但是,您可以通过将 x 移动原始高度差并将 y 移动原始宽度差来轻松调整它。

于 2013-03-25T03:32:36.117 回答
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");
    }
}
于 2013-03-26T22:26:40.503 回答
0

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

有关使用边界的示例,请参见此处:iPhone 正确的横向窗口坐标

于 2013-03-25T02:02:30.227 回答