我的iOS应用中有UIToolbar
我使用willRotateToInterfaceOrientation
在CGAffineTransform
函数中以编程方式轮换。变换需要缩放,旋转和平移。我知道如何计算比例和旋转参数,但似乎无法弄清楚如何计算转换参数,以便视图将在正确的位置。经过大量的反复试验后,我确定了将视图移动到iPad上正确位置的确切数字,但我不想对数字进行硬编码,以免代码与设备相关。如何计算翻译参数?
这是我在包含工具栏的视图控制器中的当前willRotateToInterfaceOrientation
函数。我想知道如何计算下面的tx
和ty
,而不是对值进行硬编码。我尝试使用工具栏和窗口大小的各种功能,但工具栏总是出现在窗口中的一个奇怪位置或窗口外部,而不是窗口底部。
- (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模拟器上进行测试。
答案 0 :(得分:2)
使用CGAffineTransform,你需要非常小心按照你做的顺序和(正如大卫提到的那样)你需要知道你正在旋转或调整大小的位置。人们常常对旋转的结果感到惊讶,无论anchorPoint(你用仿射旋转的点)都会感到惊讶。
如果我们在初始视图中有这个:
您可以将anchorPoint设置为(0,0)或图像的中心。要么可以工作。单独的,也不会给你你想要的东西。
这是从0,0:
如果您只想旋转视图以显示它,如果您有0,0锚点,则旋转并想知道“我的视图发生了什么!!!”但是你可以把它翻译成你想要的地方。
这是设置在图像中心的锚点:
您可以看到生成的视图,但它的垂直和水平都是关闭的,因为它是一个矩形,并且边长不同。但是,您可以通过将x移动原始高度差并将y移动原始宽度差来轻松调整。
答案 1 :(得分: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];
以下是更正后的代码,其中转换参数tx
和ty
是根据工具栏和窗口大小计算的,而不是硬编码。我将逻辑包装在新的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");
}
}
答案 2 :(得分:0)
从UIView类引用:transform - 指定应用于接收器的变换,相对于其边界的中心。这意味着您的CGAffineTransform围绕框的中心旋转,而不是框架原点。您需要相对于其从旋转的位移偏移原点。这些数字是边界框尺寸的函数。
有关使用边界的示例,请参阅此处:iPhone correct landscape window coordinates