从UITabBarController继承/持久化UINavigationBar跨子视图控制器?

3
我创建了一个UITabBarController,并将多个UIViewControllers用作标签。我还想在这个UITabBarController中显示一个常量 UINavigationBar,该导航栏将跨选项卡保持不变。由于UITabBarController的所有子视图控制器都不是UINavigationControllers,因此我希望这是可能的,而且不会引起冲突。有没有办法做到这一点?
目前,我正在将完全相同的UINavigationBar和相应的逻辑复制粘贴到每个子视图控制器中,所以我猜肯定有更好的方法。
就其有用性而言:想象一下包含两个UIBarButtonItems“注销”和“账户”的UINavigationBar。按“注销”将注销您并推出模态视图控制器,以便您可以重新登录,而按“账户”将打开一个带有您的帐户信息的模态视图控制器。
我在Stack Overflow上找到了这个现有的问题,但是问题本身和它的评论都措辞不当且信息匮乏:How to add a navigation bar to UITabBarController? 更新:
我感觉我可能没有解释清楚,所以我创建了一个图形来说明我想要做的事情。在下面的图像中,我有一个UITabBarController,它是用四个UIViewController作为子级呈现的模态方式。我希望有一个恒定的UINavigationBar,以便用户按下Done来解除模态呈现的视图控制器。由于每个子视图控制器都是单个视图,因此似乎没有必要为了使UINavigationBar出现而将每个子视图控制器嵌入其自己的UINavigationController中。当前,在以模态方式呈现UITabBarController时,不会出现导航栏。

你复制/粘贴的代码是什么?我已经分别使用storyboard和代码设置了它,不需要复制/粘贴。 - Jeffery Thomas
目前,我正在每个子视图控制器中创建一个IBAction,并将其委托回UITabBarController。这不像也复制主要逻辑一样浪费/重复,但它仍然似乎应该更加DRY。 - Ken M. Haggerty
好的,我必须重新阅读一下。您希望为所有选项卡使用单个导航控制器。当您点击选项卡图标时会发生什么?它只是将一个新的视图控制器推送到导航控制器上还是替换顶部的视图控制器? - Jeffery Thomas
我不想在所有选项卡中只使用一个UINavigationController,而是只想要一个单独的UINavigationBar。目前,如果我在界面构建器中将一个UINavigationBar添加到UITabBarController中,它根本不会显示出来。然而,我可以让每个选项卡连接到同一个UINavigationController,并覆盖-[UITabBarController tabBarController:shouldSelectViewController:]方法,以便它仅更新UINavigationController的子视图,而不是完全替换它。 - Ken M. Haggerty
这可以在Interface Builder中完成。我工作的第一个项目就是这样做的。如果它是一个故事板,那么非常简单,如果它是一个NIB,则有点棘手,但仍然可行。 - Jeffery Thomas
我会继续尝试,但您能否在下面作为答案发布您是如何做到的呢?或者当您将UINavigationBar拖放到Interface Builder中的UITabBarController时,它是否已经为您工作了? - Ken M. Haggerty
3个回答

3

我终于把这个弄好了!

简而言之:您无法继承或保存UINavigationBar,但是您可以通过其UINavigationItem设置其UIBarButtonItem

以下是我所做的事情,相关代码和注意事项:

我所做的:

1)子类化UITabBarController:我创建了自己的UITabBarController的自定义子类。由于我希望我的两个UIBarButtonItem跨选项卡保持不变,因此我还在UITabBarController子类中创建了@property。由于我还希望我的UITabBarController处理这些UIBarButtonItem,因此我还在我的UITabBarController子类中为每个按钮创建了一个方法。

2)嵌入视图控制器以用于选项卡中的UINavigationControllers:即使您不想或不需要UINavigationController,这也是获得所需效果的最佳方法,因为在iOS 7中,导航栏向上延伸到状态栏下方,直至屏幕顶部。 (就我所能探索的而言,在iOS 7中,我无法使用没有UINavigationControllerUINavigationBar复制此效果。)

3)将UIBarButtonItems分配给顶部视图控制器:我通过我的选项卡栏控制器的- [initWithNibName:bundle:] - [awakeFromNib] 方法将UITabBarController子类的UIBarButtonItem分配给所需的视图控制器。我通过遍历通过self.viewControllers分配为选项卡的所有视图控制器;检查它们是否是UINavigationController;并通过[navigationController.topViewController.navigationItem setLeftBarButtonItem:self.leftBarButton][navigationController.topViewController.navigationItem setRightBarButtonItem:self.RightBarButton]将我的UIBarButtonItem分配给其导航控制器的顶部视图控制器的导航项。

相关代码:

// MyTabBarController.h //

@interface MyTabBarController () <UITabBarControllerDelegate>
@property (nonatomic, strong) UIBarButtonItem *leftBarButton;
@property (nonatomic, strong) UIBarButtonItem *rightBarButton;
- (void)setup;
- (void)buttonLeft:(UIBarButtonItem *)sender;
- (void)buttonRight:(UIBarButtonItem *)sender;
@end

@implementation MyTabBarController

@synthesize leftBarButton = _leftBarButton;
@synthesize rightBarButton = _rightBarButton;

- (UIBarButtonItem *)leftBarButton
{
    if (!_leftBarButton) _leftBarButton = [[UIBarButtonItem alloc] initWithTitle:@"Left" style:UIBarButtonItemStyleBordered target:self action:@selector(buttonLeft:)];
    return _leftBarButton;
}

- (UIBarButtonItem *)rightBarButton
{
    if (!_rightBarButton) _rightBarButton = [[UIBarButtonItem alloc] initWithTitle:@"Right" style:UIBarButtonItemStyleBordered target:self action:@selector(buttonRight:)];
    return _rightBarButton;
}

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    if (self)
    {
        [self setup];
    }
    return self;
}

- (void)awakeFromNib
{
    [self setup];
}

- (void)setup
{
    [self setDelegate:self]; // since I am using myself as the UITabBarControllerDelegate
    for (UIViewController *viewController in self.viewControllers)
    {
        if ([viewController isKindOfClass:[UINavigationController class]])
        {
            UINavigationController *navigationController = (UINavigationController *)viewController;
            [navigationController.topViewController.navigationItem setLeftBarButtonItem:self.leftBarButton];
            [navigationController.topViewController.navigationItem setRightBarButtonItem:self.rightBarButton];
        }
    }
}

- (void)buttonLeft:(UIBarButtonItem *)sender
{
    // Left bar button action
}

- (void)buttonRight:(UIBarButtonItem *)sender
{
    // Right bar button action
}

@end

注意事项:

在解决方案的过程中,我学到了很多东西,包括以下内容:

  • 无法以编程方式设置自己的UINavigationBar。 最好的方法是创建您自己的自定义UINavigationBar子类,并通过接口构建器/故事板将其设置为您的UINavigationController,如此处所述:(外部网站:iOS Dev Notes)
  • 无法从XIB创建和使用UINavigationBar,如此处所述:如何使用XIB自定义UINavigationBar?
  • 在UIViewController中拖入UINavigationBar和UITabBar来组合"UITabBarController/UINavigationController"不适用于iOS 7,因为iOS 7使用重叠的半透明UI元素,其重叠不能很好地以编程方式设置(例如,导航栏的扩展超出状态栏后面)。
  • 将UITabBarController嵌入UINavigationController中可行但被苹果文档明确禁止/不建议使用。 这是因为在一个UI中嵌入顶级导航功能(即,允许用户在平行界面之间切换的UITabBarController)是不好的逻辑,而该UI应该能够推送和弹出其视图控制器。

那么我的问题是:当您遍历选项卡栏的选项卡时,每次都会调用(void)setup吗?如果是,那么您对注销或帐户做了相同的事情。那就是你在问的东西。我的意思是,尽管这些按钮对于所有VC来说感觉很通用,在后台遍历选项卡时,您仍然一遍又一遍地调用相同的函数! - Nayan
我很难理解你的问题,如果我没有回答你的问题,请澄清一下。我通过我的 UITabBarController 设置了我想要的 UIBarButtonItem 并将 UIBarButtonItem 的目标设置为 self(即我的 UITabBarController)。这样,我所有关于 UIBarButtonItem 的逻辑都在我的 UITabBarController 中,无需复制粘贴! - Ken M. Haggerty

0

好的,这有点老派,但这是我所做的。

首先,我创建了一个 Nib 来容纳选项卡控制器。我通过创建一个空的 Nib 并将选项卡控制器拖入其中作为第一项来实现这一点。选项卡控制器预先填充了两个视图控制器。这些可以被删除。

接下来,将您需要的所有导航控制器拖入选项卡控制器中。您应该小心确保它们是选项卡控制器的子级,而不是选项卡控制器的同级。

导航控制器预先填充了一个视图控制器。对于每个视图控制器,请选择该视图控制器。在属性检查器中,将 NIB 名称设置为您的视图控制器的 NIB(或者您可以在身份检查器中设置自定义类)。

最后,在 -application:didFinishLaunchingWithOptions: 中加载选项卡控制器。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"JLTRootController" owner:nil options:nil];
    UIViewController *controller = views[0];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    self.window.backgroundColor = [UIColor whiteColor];
    self.window.rootViewController = controller;
    [self.window makeKeyAndVisible];
    return YES;
}

希望这有所帮助。

这并不是我想要的,可能是我表达问题的方式不正确。我希望在UITabBarController的所有子视图控制器中都有相同的 UINavigationBar。根据我在遵循您的代码时所能实现的内容,我只是拥有多个不同的UINavigationController,每个导航控制器都有自己独特的UINavigationBar - Ken M. Haggerty

0

我知道这个主题已经开始讨论和回答了一段时间,但我想做出贡献:

  • 在UITabBarController之前向你的故事板添加UINavigationController。使用控制拖动将UITabBarController设置为其根视图控制器。这个UINavigationController将作为顶部的持久导航栏。
  • 对于每个选项卡,将UINavigationController设置为选项卡的视图控制器,并将现有的视图控制器链接为其根视图控制器。
  • 对于每个选项卡,进入UINavigationController的设置并取消选中“显示导航栏”的框。这将隐藏内部导航栏。

现在你有了一个顶部的持久栏(由UITabBarController之前的UINavigationController控制),而且不需要编写任何代码 :) 一切都在storyboards中完成。您甚至可以将按钮拖动到它上面以及自定义标题视图。

如果你想通过编程方式访问它:

  • 你可以子类化UITabBarController。然后self.navigationItem.rightBarButton应该允许您轻松访问和设置按钮。
  • 您还可以直接从选项卡视图控制器中使用`self.tabbarcontroller.navigationitem.rightBarButton'设置它。因此,它是一种轻微的间接水平。

简单易懂! 这个答案基于我的之前的一个回答,如果你想要更多细节和图片,可以在这里查看: 如何使iPhone Facebook应用程序的导航栏固定


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接