1

UIToolbariOSアプリに、を使用して関数内でプログラムで回転するがありwillRotateToInterfaceOrientationますCGAffineTransform。変換には、スケーリング、回転、および平行移動が必要です。スケールと回転のパラメーターを計算する方法は知っていますが、ビューが正しい位置に表示されるように平行移動パラメーターを計算する方法を理解できないようです。試行錯誤の末、iPadの適切な場所にビューを移動するための正確な数値を決定しましたが、コードがデバイスに依存しすぎないように、数値をハードコーディングしないことをお勧めします。変換パラメータを計算するにはどうすればよいですか?

これが、ツールバーを含むViewControllerの現在の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シミュレーターの両方でテストしています。

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 に答える