iOS:导航栏的titleView在手机旋转时无法正确resize

我正在编写一个iPhone应用程序(与大多数应用程序一样)支持自动旋转:您可以旋转手机,其视图可以旋转并适当resize。

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

我知道你在想什么,“只需将其autoresizingMask设置为UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ,”但它并不那么简单。 当然,如果我没有设置我的视图的autoresizingMask ,那么我的视图不会resize; 我希望它能够resize。

问题是,如果我设置了autoresizingMask ,那么只要该视图可见,它就会正确resize; 但是在这种情况下titleView的大小搞砸了:

  1. 将手机保持在纵向模式下运行应用程序。 一切都很好看。
  2. 做一些导致应用程序将另一个视图推送到导航堆栈的东西。 例如,单击导致调用[self.navigationController pushViewController:someOtherViewController animated:YES]的表行或按钮。
  3. 在查看子控制器时,将手机旋转到横向。
  4. 单击“返回”按钮返回顶级视图。 此时,标题视图混乱:尽管您正在横向模式下握住手机,但标题视图的大小仍然像在纵向模式下一样。
  5. 最后,将手机旋转回纵向模式。 现在事情变得更糟了:标题视图缩小了尺寸(因为导航栏变小了),但由于它已经太小了,现在它太小了。

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

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

那么……我做错了什么? 有没有办法让我的titleView始终正确resize?

您还应该设置UIImageViewcontentMode ,以便在横向和/或纵向模式下正确显示titleView

imgView.contentMode=UIViewContentModeScaleAspectFit;

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

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

我有类似的东西 – 但它返回(弹出)到根视图控制器。 最后,我选择以下内容进行弹出:

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

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

我通过跟踪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]; } } 

(回答我自己的问题)

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

我们的想法是,我们不会将titleView恢复到之前的确切大小; 相反,我们正在恢复它,以便它具有与之前相同的边距 。 这样,如果手机已旋转,则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; } } } 

对于IOS5以后,因为这是一个老问题…这就是我如何完成相同的问题,标题文本没有正确对齐。

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

在ios5 / 6 sims上测试工作正常。

这就是我做的:

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

viewTitle是在xib中创建的视图,它采用navigationBar的大小,在添加后,titleViewresize以留出空间到后退按钮。 轮换似乎工作正常。

我有同样的问题,但我似乎得到了以下代码的解决方法。

 - (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,但我希望这会对你有所帮助。