首页 文章

iOS:导航栏's titleView doesn' t在手机旋转时正确调整大小

提问于
浏览
13

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

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

我知道你在想什么,“只需将它的 autoresizingMask 设置为 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight ”,但它设置了我的视图 autoresizingMask ,然后我的视图没有调整大小;我希望它能够调整大小 .

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

  • 运行应用程序,手机处于纵向模式 . 一切都很好看 .

  • 做一些导致应用程序将另一个视图推送到导航堆栈的内容 . 例如 . 单击导致调用 [self.navigationController pushViewController:someOtherViewController animated:YES] 的表格行或按钮 .

  • 查看子控制器时,将手机旋转至横向 .

  • 单击"Back"按钮返回顶级视图 . 此时, Headers 视图混乱:尽管您正在横向模式下握住手机,但 Headers 视图的大小仍然像在纵向模式下一样 .

  • 最后,将手机旋转回纵向模式 . 现在事情变得更糟了: Headers 视图缩小了尺寸(因为导航栏变小了),但由于它已经太小了,现在它太小了 .

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

  • 使用Xcode的"Navigation-based Application"向导制作应用程序 .

  • 进行设置,以便顶级表视图具有行,当您单击它们时,将详细视图推送到导航堆栈 .

  • 在顶级视图控制器和详细视图控制器中包含此代码:

- (BOOL)shouldAutorotateToInterfaceOrientation:
        (UIInterfaceOrientation)interfaceOrientation {
    return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
  • 仅在顶级视图控制器中包含此代码:
- (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;
}
  • 最后,按照我的重复步骤 .

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

7 回答

  • 2

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

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

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

  • 7

    您还应该设置 UIImageViewcontentMode 以使横向和/或纵向模式正确显示 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];
    
  • 2

    我通过跟踪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恢复到之前的确切大小;相反,我们正在恢复它,以便它具有与之前相同的边距 . 这样,如果手机已旋转,则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;
            }
        }
    }
    
  • 0

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

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

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

  • 1

    这就是我做的:

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

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

  • 3

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

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

相关问题