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

时间:2011-01-14 05:03:38

标签: iphone ios uinavigationbar

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

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

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

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

  1. 运行应用程序,手机处于纵向模式。一切都很好。
  2. 做一些导致应用将另一个视图推送到导航堆栈的内容。例如。单击导致调用[self.navigationController pushViewController:someOtherViewController animated:YES]的表格行或按钮。
  3. 在查看子控制器时,将手机旋转至横向。
  4. 点击“返回”按钮返回顶级视图。此时,标题视图混乱:虽然您正在横向模式下拿着手机,但标题视图的大小仍然像在纵向模式下一样。
  5. 最后,将手机旋转回纵向模式。现在事情变得更糟了:标题视图的大小缩小了(因为导航栏变小了),但由于它已经太小了,现在很多太小了。
  6. 如果您想自己重现,请按照以下步骤操作(这有点工作):

    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. 最后,按照我的重复步骤进行操作。

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

7 个答案:

答案 0 :(得分:7)

您还应该设置contentMode的{​​{1}},以便在横向和/或纵向模式下正确显示UIImageView

titleView

整个序列:( self是imgView.contentMode=UIViewContentModeScaleAspectFit;实例)

UIViewController

答案 1 :(得分:4)

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

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

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

答案 2 :(得分:3)

我通过跟踪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 :(得分:2)

(回答我自己的问题)

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

答案 4 :(得分:2)

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

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

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

答案 5 :(得分:1)

这就是我所做的:

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

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

答案 6 :(得分:0)

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

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