【问题标题】:iOS: navigation bar's titleView doesn't resize correctly when phone rotatesiOS:手机旋转时导航栏的 titleView 无法正确调整大小
【发布时间】:2023-03-04 05:02:01
【问题描述】:

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

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

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

问题是,如果我确实设置了它的autoresizingMask,那么只要该视图可见,它就会正确调整大小;但是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. 最后,按照我的重现步骤。

那么……我做错了吗?有没有办法让我的 titleView 始终正确调整大小?

【问题讨论】:

    标签: iphone ios uinavigationbar


    【解决方案1】:

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

    <b>imgView.contentMode=UIViewContentModeScaleAspectFit;</b>

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

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

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案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];
             }
         }
        

        【讨论】:

          【解决方案4】:

          (回答我自己的问题)

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

          【讨论】:

          • 我可能为时已晚(4-5 年,哈哈),但我可以问一下......在 AutoLayout 中......这种“不调整大小”的事情还会发生在你身上吗?我正在使用 Storyboards 和 UINavigationControllers,当我设置一个非常长的标题(比如说...... 100 个字符或更多)时,当旋转 iPhone 时效果很好,但在某些时候它只是保持标题视图的横向大小旋转为纵向。我不知道该怎么办。如果没有个性化(在没有自定义类的情况下拖动 Navcontroller+Root 视图控制器),这仍然会发生。这让我发疯,看起来像一个 iOS 错误......
          【解决方案5】:

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

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

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

          【讨论】:

            【解决方案6】:

            这就是我所做的:

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

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

            【讨论】:

              【解决方案7】:

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

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-08-19
                • 2018-01-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多