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