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

Mik*_*rty 13 iphone uinavigationbar ios

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

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

我知道你在想什么,"只要设置了autoresizingMaskUIViewAutoresizingFlexibleWidth | 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);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 最后,按照我的repro步骤.

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

yon*_*nel 7

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

__CODE__

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

UIImageView* imgView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"myCustomTitle.png"]];
imgView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
imgView.contentMode=UIViewContentModeScaleAspectFit;
self.navigationItem.titleView = imgView;
[imgView release];
Run Code Online (Sandbox Code Playgroud)


Mik*_*rty 2

(回答我自己的问题)

我通过手动跟踪 titleView 的边距(它与导航栏边缘的距离)来完成这项工作 - 当视图消失时保存,并在视图重新出现时恢复。

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

这是我的代码:

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

@interface MyViewController ...
{
    CGRect titleSuperviewBounds;
    UIEdgeInsets titleViewMargins;
}
Run Code Online (Sandbox Code Playgroud)

在我的视图控制器的 .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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)