web-dev-qa-db-ja.com

iOS:電話が回転するとナビゲーションバーのtitleViewのサイズが正しく変更されない

私は(ほとんどのアプリと同様に)自動回転をサポートするiPhoneアプリを書いています。電話を回転させると、そのビューが適切に回転およびサイズ変更されます。

しかし、カスタムビューをnavigationItem.titleView(ナビゲーションバーのタイトル領域)に割り当てているので、電話が回転したときにそのビューのサイズを正しく変更できません。

autoresizingMaskUIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeightに設定するだけです」とあなたが考えていることは知っていますが、それほど単純ではありません。もちろん、ビューのautoresizingMaskを設定しないと、ビューのサイズは変更されません。サイズを変更したい。

問題は、I doautoresizingMaskを設定すると、そのビューが表示されている限り、正しくサイズ変更されることです。しかし、このシナリオではtitleViewのサイズが台無しになります。

  1. スマートフォンを縦向きモードにしてアプリを実行します。すべてがよさそうだ。
  2. アプリが別のビューをナビゲーションスタックにプッシュするような操作を行います。例えば。 [self.navigationController pushViewController:someOtherViewController animated:YES]を呼び出すテーブルの行またはボタンをクリックします。
  3. 子コントローラーを見ながら、電話を横向きに回転させます。
  4. 「戻る」ボタンをクリックして、トップレベルのビューに戻ります。この時点で、タイトルビューはめちゃくちゃになっています。電話を横向きモードで持っていても、タイトルビューのサイズは縦向きモードで持っているかのようになります。
  5. 最後に、電話機を回転させてポートレートモードに戻します。事態はさらに悪化します。タイトルビューのサイズは縮小しますが(ナビゲーションバーが小さくなったため)、すでに小さすぎたため、現在は非常に小さすぎます。

これを自分で再現したい場合は、次の手順に従ってください(これは少し作業です)。

  1. Xcodeの「ナビゲーションベースのアプリケーション」ウィザードを使用してアプリを作成します。
  2. トップレベルのテーブルビューに行が含まれるように設定します。行をクリックすると、詳細ビ​​ューがナビゲーションスタックにプッシュされます。
  3. このコードをトップレベルのビューコントローラーと詳細ビューコントローラーの両方に含めます。

    - (BOOL)shouldAutorotateToInterfaceOrientation:
            (UIInterfaceOrientation)interfaceOrientation {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    }
    
  4. このコードは、トップレベルのViewControllerにのみ含めてください。

    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // Create "Back" button
        UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithTitle:@"Master"
            style:UIBarButtonItemStylePlain target:nil action:nil];
        self.navigationItem.backBarButtonItem = backButton;
        [backButton release];
    
        // Create title view
        UILabel* titleView = [[[UILabel alloc] initWithFrame:CGRectMake(0,0,500,38)] autorelease];
        titleView.textAlignment = UITextAlignmentCenter;
        titleView.text = @"Watch this title view";
    
        // If I leave the following line turned on, then resizing of the title view
        // messes up if I:
        //
        // 1. Start at the master view (which uses this title view) in portrait
        // 2. Navigate to the detail view
        // 3. Rotate the phone to landscape
        // 4. Navigate back to the master view
        // 5. Rotate the phone back to portrait
        //
        // On the other hand, if I remove the following line, then I get a different
        // problem: The title view doesn't resize as I want it to when I:
        //
        // 1. Start at the master view (which uses this title view) in portrait
        // 2. Rotate the phone to landscape
        titleView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    
        self.navigationItem.titleView = titleView;
    }
    
  5. 最後に、私の再現手順に従います。

だから...私は何か間違ったことをしていますか? titleViewのサイズを常に正しく変更する方法はありますか?

13
Mike Morearty

(私自身の質問に答える)

TitleViewのマージン(ナビゲーションバーの端からの距離)を手動で追跡することでこれを機能させました-ビューが消えたときに保存し、ビューが再び表示されたときに復元します。

アイデアは、titleViewを以前の正確なサイズに復元しないということです。むしろ、以前と同じmarginsになるように復元しています。そうすれば、電話が回転した場合、titleViewは新しい適切なサイズになります。

これが私のコードです:

私のビューコントローラーの.hファイル:

@interface MyViewController ...
{
    CGRect titleSuperviewBounds;
    UIEdgeInsets titleViewMargins;
}

私のビューコントローラーの.mファイル:

/**
 * Helper function: Given a parent view's bounds and a child view's frame,
 * calculate the margins of the child view.
 */
- (UIEdgeInsets) calcMarginsFromParentBounds:(CGRect)parentBounds
                                  childFrame:(CGRect)childFrame {
    UIEdgeInsets margins;
    margins.left = childFrame.Origin.x;
    margins.top = childFrame.Origin.y;
    margins.right = parentBounds.size.width -
        (childFrame.Origin.x + childFrame.size.width);
    margins.bottom = parentBounds.size.height -
        (childFrame.Origin.y + childFrame.size.height);
    return margins;
}

- (void)viewDidUnload {
    [super viewDidUnload];

    titleSuperviewBounds = CGRectZero;
    titleViewMargins = UIEdgeInsetsZero;
}

- (void) viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];

    // Keep track of bounds information, so that if the user changes the
    // phone's orientation while we are in a different view, then when we
    // return to this view, we can fix the titleView's size.
    titleSuperviewBounds = self.navigationItem.titleView.superview.bounds;
    CGRect titleViewFrame = self.navigationItem.titleView.frame;
    titleViewMargins = [self calcMarginsFromParentBounds:titleSuperviewBounds
                                              childFrame:titleViewFrame];
}


- (void) viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];

    // Check for the case where the user went into a different view, then
    // changed the phone's orientation, then returned to this view.  In that
    // case, our titleView probably has the wrong size, and we need to fix it.
    if (titleSuperviewBounds.size.width > 0) {
        CGRect newSuperviewBounds =
            self.navigationItem.titleView.superview.bounds;
        if (newSuperviewBounds.size.width > 0 &&
            !CGRectEqualToRect(titleSuperviewBounds, newSuperviewBounds))
        {
            CGRect newFrame = UIEdgeInsetsInsetRect(newSuperviewBounds,
                titleViewMargins);
            newFrame.size.height =
                self.navigationItem.titleView.frame.size.height;
            newFrame.Origin.y = floor((newSuperviewBounds.size.height -
                self.navigationItem.titleView.frame.size.height) / 2);
            self.navigationItem.titleView.frame = newFrame;
        }
    }
}
2
Mike Morearty

また、contentModeUIImageViewを設定して、titleViewが横向きまたは縦向きモードで正しく表示されるようにする必要があります。

imgView.contentMode=UIViewContentModeScaleAspectFit;

シーケンス全体:(自己はUIViewControllerインスタンスです)

UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"myCustomTitle.png"]];
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imgView.contentMode=UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imgView;
[imgView release];
7
yonel

私は似たようなものを持っていました-しかしそれはルートビューコントローラーに戻っていました(ポップしていました)。最終的に、私はポップのために以下を使用しました:

[[self navigationController] setNavigationBarHidden:YES animated:NO];
[[self navigationController] popViewControllerAnimated:YES];
[[self navigationController] setNavigationBarHidden:NO animated:NO];

そしてそれはうまくいった。もっと良い方法があったかもしれませんが、私がこの問題にすでに費やしたすべての時間の後、これは私にとって十分でした。

4
westsider

この同じ問題に対処するために、customViewの初期フレームを追跡し、UIButtonサブクラスの-setLandscapeメソッドでそれと初期フレームのスケーリングされたCGRectを切り替えました。 UIButtonサブクラスをnavigationItem.titleViewおよびnavigationItem.rightBarButtonItemとして使用しました。

UIButtonサブクラス内-

- (void)setLandscape:(BOOL)value
 {
     isLandscape = value;

     CGFloat navbarPortraitHeight = 44;
     CGFloat navbarLandscapeHeight = 32;

     CGRect initialFrame = // your initial frame
     CGFloat scaleFactor = floorf((navbarLandscapeHeight/navbarPortraitHeight) * 100) / 100;

     if (isLandscape) {
         self.frame = CGRectApplyAffineTransform(initialFrame, CGAffineTransformMakeScale(scaleFactor, scaleFactor));
     } else {
         self.frame = initialFrame;
     }
 }

次に、InterfaceOrientationデリゲートで、customViewsの-setLandscapeメソッドを呼び出してサイズを変更しました。

UIViewControllerの場合-

- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration 
{
    [self updateNavbarButtonsToDeviceOrientation];;
}

- (void)updateNavbarButtonsToDeviceOrientation
 {
     ResizeButton *rightButton = (ResizeButton *)self.navigationItem.rightBarButtonItem.customView;
     ResizeButton *titleView = (ResizeButton *)self.navigationItem.titleView;

     if (self.interfaceOrientation == UIDeviceOrientationPortrait || self.interfaceOrientation == UIDeviceOrientationPortraitUpsideDown) {
         [rightButton setLandscape:NO];
         [titleView setLandscape:NO];
     } else {
         [rightButton setLandscape:YES];  
         [titleView setLandscape:YES];
     }
 }
3
Nate Potter

IOS5以降の場合、これは古い質問であるため...これは、タイトルテキストが正しく配置されないという同じ問題をどのように達成したかです。

[[UINavigationBar appearance] setTitleVerticalPositionAdjustment:2 forBarMetrics:UIBarMetricsLandscapePhone];

Ios5/6シムでテスト済みは正常に動作します。

2
StuartM

これは私がしたことです:

self.viewTitle.frame = self.navigationController.navigationBar.frame;
self.navigationItem.titleView = self.viewTitle;

ViewTitleは、xibで作成されたビューであり、navigationBarのサイズを取り、titleViewが追加された後、サイズを調整して、戻るボタンに余裕を持たせます。回転はうまく機能しているようです。

1
LightMan

同じ問題が発生しましたが、次のコードで回避策が得られるようです。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];

    UIView *urlField = self.navigationItem.leftBarButtonItem.customView;
    CGRect frame = urlField.frame;
    frame.size.width = 1000;
    urlField.frame = frame;
}

私の場合、カスタムビューはUITextFieldですが、これがお役に立てば幸いです。

0
hiroshi