弹出 UIViewController 会导致之前的 UIViewControllers View 改变位置

Popping UIViewController causes previous UIViewControllers View to change position

我有一个 UINavigationController 和一个 UIViewController 设置为它的根控制器,它在它的 UIView 上包含一个背景,使用一个图像集就在导航栏下面。然后我将一个新的 UIViewController 推到导航控制器上,当按下后退按钮时,以前的控制器看起来不同。使用可视化调试器,我可以看到 self.view 已经完全向下移动到导航栏下方,而之前它位于顶部。我不知道并绞尽脑汁想知道为什么会这样

-(void)pushIPhoneMessagingContactsController:(MessageContactsViewController *)contactsController{
    self.selectorView.hidden = YES;
    [self.navigationController pushViewController:contactsController animated:YES];
}

在 RootViewController (iPhoneMessagingNotificationsController) 上

-(void)viewWillAppear:(BOOL)animated{
    self.selectorView.hidden = NO;
    [[[self navigationItem] leftBarButtonItem] setTintColor:[UIColor blackColor]];
    [[UIApplication sharedApplication]      setStatusBarStyle:UIStatusBarStyleDefault];

    if ([_displayType intValue] == MESSAGES_SHOWING) {
        [self.notificationsViewController.view removeFromSuperview];
        [self.contentView addSubview:_messagesViewController.view];
    } else {
        [self.messagesViewController.view removeFromSuperview];
        [self.contentView addSubview:_notificationsViewController.view];
    }

}

似乎有问题的行出现在推送的 UIViewController 的 viewWillAppear 方法中

self.navigationController.navigationBar.translucent = 是;

这个导航栏在其他地方被设置为半透明:

 [self.navigationController.navigationBar setBackgroundImage:[UIImage new]
                                              forBarMetrics:UIBarMetricsDefault];
 self.navigationController.navigationBar.shadowImage = [UIImage new];
 self.navigationController.navigationBar.translucent = YES;

并使其再次变为纯色:

 self.navigationController.navigationBar.shadowImage = nil;
 self.navigationController.navigationBar.translucent = NO;

但这段代码似乎与布局混乱,所以也许有另一种方法可以在不影响布局的情况下更改导航栏和状态栏的不透明度?

您目前正在尝试做的是隐藏或显示一个 selectorView,它实际上只应该出现在一个特定的视图控制器中。

这是解决此问题的封装方法,它使您的 selectorView 成为根视图控制器的一部分,从而删除与其他视图控制器的连接。他们不再需要知道或隐藏它。

将您的 selectorView 添加到 rootViewController 的导航栏 titleView。 (您可以在代码中执行此操作,或将其放入情节提要中并为其添加一个 IBOutlet。)

self.navigationItem.titleView = selectorView;

现在,当您按下另一个视图控制器时,它的标题将替换您的 rootViewController 的 selectorView 标题(视图)。您的其他视图控制器不需要了解该视图的任何信息。

总的来说,这是一个很好的设计方法。任何时候你有一个应该只出现在一个视图控制器的导航栏上的控件,你想让它成为该视图控制器的一部分 navigationItem (titleView,或 left/right 栏按钮项目。)iOS 将在显示该视图控制器时显示该控件,并在该视图控制器不再是导航控制器堆栈中的顶部视图控制器时隐藏该控件。

至于 64 像素高度问题,这可能与 rootViewController 层次结构中不应该存在的某些复杂性有关。

在 iOS 7/8 中,视图的内容默认显示在半透明的导航栏下方。 Apple 通过插入视图层次结构的第一个视图,免费为您管理。

从您的代码看来,您正在尝试 "hide" 或 "show"(未)选择 viewController 的视图。

每个视图控制器都应该有一个它控制的视图。视图控制器不应试图控制其他视图控制器的 views,或将其他视图控制器的 views 添加到它自己的视图层次结构中。

这是 Apple 推荐的解决方法。在您的 rootViewController 中使用 containerView。容器视图的全部目的是将视图控制器封装在视图中。当您的 selectorView 更改要显示的视图时,您的容器视图会从一个视图控制器过渡到另一个视图控制器。 (如果您不熟悉如何操作,请查看此 answer。)

将 containerView 固定到 rootViewController 的视图,以便自动布局可以为您调整大小。

您的视图层次结构现在看起来像视图 -> containerView,而不是视图 -> 未选择的视图控制器的隐藏视图,已选择的视图控制器的显示视图。 Apple 可以调整第一个视图的插图,并且不会有任何错误偏移(按导航控件的高度)。

更新:

question 讨论 scrollViewInsets 以及如何在 view-controller-by-view-controller 基础上设置它们。如果您有视图控制器,并且不希望其内容出现在栏下,请取消选中该框。

但处理此问题的最佳方法是 "standardize" 您的 UI,因此它不会因视图而异。要么使条形图始终半透明,要么不始终半透明。这使得用户的转换更少 "jarring"。