iOS:手机旋转时导航栏的 titleView 无法正确调整大小

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4688137/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:31:51  来源:igfitidea点击:

iOS: navigation bar's titleView doesn't resize correctly when phone rotates

iphoneiosuinavigationbar

提问by Mike Morearty

I'm writing an iPhone app that (like most apps) supports auto-rotation: You rotate your phone, and its views rotate and resize appropriately.

我正在编写一个 iPhone 应用程序(像大多数应用程序一样)支持自动旋转:你旋转你的手机,它的视图会适当地旋转和调整大小。

But I am assigning a custom view to navigationItem.titleView(the title area of the navigation bar), and I can't get that view to resize correctly when the phone rotates.

但是我正在为navigationItem.titleView(导航栏的标题区域)分配一个自定义视图,当手机旋转时,我无法让该视图正确调整大小。

I know what you're thinking, "Just set its autoresizingMaskto UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight," but it's not that simple. Of course, if I don't set my view's autoresizingMask, then my view doesn't resize; and I want it to resize.

我知道您在想什么,“只需将其设置autoresizingMaskUIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight”,但这并没有那么简单。当然,如果我不设置视图的autoresizingMask,那么我的视图不会调整大小;我希望它调整大小。

The problem is, if I doset its autoresizingMask, then it resizes correctly as long as that view is visible; but the titleView's size gets messed up in this scenario:

问题是,如果我确实设置了它autoresizingMask,那么只要该视图可见,它就会正确调整大小;但是titleView在这种情况下 的大小会变得混乱:

  1. Run the app, with the phone held in portrait mode. Everything looks good.
  2. Do something that causes the app to push another view onto the navigation stack. E.g. click a table row or button that causes a call to [self.navigationController pushViewController:someOtherViewController animated:YES].
  3. While viewing the child controller, rotate the phone to landscape.
  4. Click the "Back" button to return to the top-level view. At this point, the title view is messed up: Although you are holding the phone in landscape mode, the title view is still sized as if you were holding it in portrait mode.
  5. Finally, rotate the phone back to portrait mode. Now things get even worse: The title view shrinks in size (since the navigation bar got smaller), but since it was already too small, now it is muchtoo small.
  1. 运行应用程序,手机处于纵向模式。一切看起来都不错。
  2. 做一些导致应用程序将另一个视图推送到导航堆栈上的事情。例如,单击导致调用的表格行或按钮[self.navigationController pushViewController:someOtherViewController animated:YES]
  3. 在查看子控制器时,将手机旋转到横向。
  4. 单击“返回”按钮返回顶层视图。此时,标题视图搞砸了:尽管您以横向模式拿着手机,但标题视图的大小仍然就像您在纵向模式下拿着它一样。
  5. 最后,将手机旋转回纵向模式。现在,事情变得更糟:标题视图的尺寸缩小(因为导航栏变得越来越小),但因为它已经是太小了,现在是多少太小。

If you want to reproduce this yourself, follow these steps (this is a bit of work):

如果你想自己重现这个,请按照以下步骤操作(这有点工作):

  1. Make an app using Xcode's "Navigation-based Application" wizard.
  2. Set it up so that the top-level table view has rows that, when you click them, push a detail view onto the navigation stack.
  3. Include this code in both the top-level view controller and the detail view controller:

    - (BOOL)shouldAutorotateToInterfaceOrientation:
            (UIInterfaceOrientation)interfaceOrientation {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    }
    
  4. Include this code in only the top-level view controller:

    - (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. Finally, follow my repro steps.

  1. 使用 Xcode 的“基于导航的应用程序”向导制作应用程序。
  2. 设置它以便顶级表视图具有行,当您单击它们时,将详细视图推送到导航堆栈上。
  3. 在顶级视图控制器和详细视图控制器中包含此代码:

    - (BOOL)shouldAutorotateToInterfaceOrientation:
            (UIInterfaceOrientation)interfaceOrientation {
        return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
    }
    
  4. 仅在顶级视图控制器中包含此代码:

    - (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. 最后,按照我的重现步骤。

So ... am I doing something wrong? Is there a way to make my titleView always resize correctly?

所以......我做错了什么吗?有没有办法让我的 titleView 始终正确调整大小?

采纳答案by Mike Morearty

(Answering my own question)

(回答我自己的问题)

I got this working by manually keeping track of the titleView's margins (its distance from the edges of the navigtion bar) -- saving when the view disappears, and restoring when the view reappears.

我通过手动跟踪 titleView 的边距(它与导航栏边缘的距离)来实现这一点——在视图消失时保存,并在视图重新出现时恢复。

The idea is, we aren't restoring the titleView to the exact size it had previously; rather, we are restoring it so that it has the same marginsit had previously. That way, if the phone has rotated, the titleView will have a new, appropriate size.

这个想法是,我们不会将 titleView 恢复到它之前的确切大小;相反,我们正在恢复它,使其具有与以前相同的边距。这样,如果手机旋转了,titleView 就会有一个新的、合适的大小。

Here is my code:

这是我的代码:

In my view controller's .h file:

在我的视图控制器的 .h 文件中:

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

In my view controller's .m file:

在我的视图控制器的 .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;
        }
    }
}

回答by yonel

You should also set the contentModeof the UIImageViewto get the titleViewproperly displayed in landscape and/or portrait mode :

你也应该设置contentModeUIImageView,以获得titleView正确显示在横向和/或纵向模式:

imgView.contentMode=UIViewContentModeScaleAspectFit;

imgView.contentMode=UIViewContentModeScaleAspectFit;

The whole sequence: (self is a UIViewControllerinstance)

整个序列:(自我是一个UIViewController实例)

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

回答by westsider

I had something similar - but it was returning (popping) to root view controller. Ultimately, I went with the following for popping:

我有类似的东西 - 但它正在返回(弹出)到根视图控制器。最终,我使用以下方法进行弹出:

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

And it worked. There may have been a better way but - after all the hours I'd already spent on this issue - this was good enough for me.

它奏效了。可能有更好的方法,但是 - 在我已经在这个问题上花费了所有时间之后 - 这对我来说已经足够了。

回答by Nate Potter

I dealt with this same issue by keeping track of the customView's initial frame, then toggling between that and a scaled CGRect of the initial frame in a -setLandscape method on a UIButton subclass. I used the UIButton subclass as navigationItem.titleView and navigationItem.rightBarButtonItem.

我通过跟踪 customView 的初始帧,然后在 UIButton 子类上的 -setLandscape 方法中的初始帧的缩放 CGRect 和该帧之间切换来处理相同的问题。我使用 UIButton 子类作为 navigationItem.titleView 和 navigationItem.rightBarButtonItem。

In UIButton subclass -

在 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;
     }
 }

Then in the InterfaceOrientation delegates I invoked the -setLandscape method on the customViews to change their sizes.

然后在 InterfaceOrientation 委托中,我调用了 customViews 上的 -setLandscape 方法来更改它们的大小。

In UIViewController -

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

回答by StuartM

For IOS5 onwards, as this is an old question...This is how I accomplished the same issue with the title text not aligning properly.

对于 IOS5 以后,因为这是一个老问题......这就是我如何解决标题文本未正确对齐的相同问题。

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

Tested on ios5/6 sims works fine.

测试在iOS5中/ 6 SIMS工作正常。

回答by LightMan

This is what I did:

这就是我所做的:

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

The viewTitle is a view created in the xib, it takes the size of the navigationBar and after it has been added the titleView adjust the size to leave room to the back button. Rotations seem to work fine.

viewTitle 是在 xib 中创建的视图,它采用导航栏的大小,并在添加后 titleView 调整大小以给后退按钮留出空间。旋转似乎工作正常。

回答by hiroshi

I had had same problem, but I seem to get workaround with following code.

我遇到了同样的问题,但我似乎可以通过以下代码解决。

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

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

In my case, the custom view is a UITextField, but I hope this will help you.

就我而言,自定义视图是一个 UITextField,但我希望这对您有所帮助。