使用多个故事板与TabBarController

8
好的,那么在开发我的最新应用程序的过程中,我发现我的Storyboard变得很大,为了清理一些内容,我将它分成了多个Storyboard,以免失控。仅仅是为了设置,我有大约20个tableviewcontrollers从根NavigationController中分支出来。那个navigationcontrollerTabBarController上的一个TabItem,它是应用程序的根视图控制器。
我已经将TabBar移动到它自己的StoryBoard中作为Root_Storyboard,并且导航控制器现在是Settings_Storyboard的初始视图。
仅仅为了测试目的,我将一些UIViewControllers放入TabBarController(Root_Storyboard)中,并对其中一个进行子类化,并将以下代码添加到其viewWillAppear方法中。它运行得很好,但我知道presentViewController会模态显示NavigationController并隐藏tabBar。显然我不想要这个,我该如何正确地推送它,以便TabBar保持可见?
- (void) viewWillAppear:(BOOL)animated {
UIStoryboard *settingsStoryboard = [UIStoryboard storyboardWithName:@"Settings_iPhone" bundle:nil];
UIViewController *rootSettingsView = [settingsStoryboard instantiateInitialViewController];

[self.tabBarController presentViewController:rootSettingsView animated:NO completion:NULL];
}

编辑 - 澄清一下。上面的代码是根据Root_iPhone.storyboard中的UIViewController (child of UITabBarController:index(1))子类方法编写的。我正在尝试加载的UINavigationController/UITableViewController位于Settings_iPhone.storyboard中。不确定如何在这种情况下实现下面建议的linkView。


你能来聊天吗?让我在那里帮助你聊天链接 - Prince Agrawal
4个回答

10

这是完全可行且明智的做法 - 简化你的Storyboard可以使接口文件更加整洁易读,减少在Xcode中的加载时间并实现更好的分组编辑。

我一直在Stack Overflow上搜寻,并注意到大家都在使用自定义转场或编程方式来实例化选项卡设置。哎呀。我拼凑出了一个简单的UIViewController子类,你可以将其用作Storyboard的占位符。

代码:

头文件:

#import <UIKit/UIKit.h>

@interface TVStoryboardViewController : UIViewController

@end

实现文件:

#import "TVStoryboardViewController.h"



@interface TVStoryboardViewController()

@property (nonatomic, strong) UIViewController *storyboardViewController;

@end



@implementation TVStoryboardViewController



- (Class)class { return [self.storyboardViewController class]; }

- (UIViewController *)storyboardViewController
{
    if(_storyboardViewController == nil)
    {

        UIStoryboard *storyboard = nil;
        NSString *identifier = self.restorationIdentifier;

        if(identifier)
        {
            @try {
                storyboard = [UIStoryboard storyboardWithName:identifier bundle:nil];
            }
            @catch (NSException *exception) {
                NSLog(@"Exception (%@): Unable to load the Storyboard titled '%@'.", exception, identifier);
            }
        }

        _storyboardViewController = [storyboard instantiateInitialViewController];
    }

    return _storyboardViewController;
}

- (UINavigationItem *)navigationItem
{
    return self.storyboardViewController.navigationItem ?: [super navigationItem];
}

- (void)loadView
{
    [super loadView];

    if(self.storyboardViewController && self.navigationController)
    {
        NSInteger index = [self.navigationController.viewControllers indexOfObject:self];

        if(index != NSNotFound)
        {
            NSMutableArray *viewControllers = [NSMutableArray arrayWithArray:self.navigationController.viewControllers];
            [viewControllers replaceObjectAtIndex:index withObject:self.storyboardViewController];
            [self.navigationController setViewControllers:viewControllers animated:NO];
        }
    }
}

- (UIView *)view { return self.storyboardViewController.view; }



@end

描述:

  1. 视图控制器使用其恢复标识符在您的项目中实例化一个故事板。
  2. 加载后,它将尝试使用故事板的初始视图控制器替换其UINavigationController的viewController数组中的自身。
  3. 在请求时,此子类将返回Storyboard的初始视图控制器的UINavigationItem。这是为了确保加载到UINavigationBars中的导航项与交换后的视图控制器相对应。

用法:

要使用它,请将其分配为UIViewController的子类,该UIViewController属于UINavigationController中的一个Storyboard中。

进入图像说明

分配恢复ID即可。

进入图像说明

设置:

以下是如何在Storyboard中设置的步骤:

显示安装图表

此设置显示带有导航控制器作为其第一个选项卡控制器的选项卡控制器。每个导航控制器都有一个简单的UIViewController作为其根视图控制器(我已将UIImageView添加到占位符中,以便更容易记住它链接到哪里)。每个都是TVStoryboardViewController的子类。每个设置为他们应该链接到的Storyboard的恢复ID。

一些优势:

  • 它似乎最适合模态演示,其中子类是导航控制器的根视图控制器。
  • 子类不会将任何控制器推入堆栈-它会进行交换。这意味着您不必手动隐藏返回按钮或在其他地方覆盖选项卡行为。
  • 如果您双击选项卡,则会像预期的那样带您进入故事板的初始视图(您不会再次看到该占位符)。
  • 非常简单的设置-无需自定义segues或设置多个子类。
  • 您可以向占位符视图控制器添加UIImageView和任何您喜欢的内容,以使您的Storyboard更清晰-它们永远不会显示出来。

一些限制:

  • 此子类需要属于UINavigationController中的某个位置。
  • 此子类仅实例化故事板中的初始视图控制器。如果要实例化链中更深的视图控制器,您可以随时进一步拆分您的故事板并重新应用此子类技巧。
  • 在推送视图控制器时,此方法效果不佳。
  • 当用作嵌入式视图控制器时,此方法效果不佳。
  • 通过segues进行消息传递可能不起作用。这种方法适用于界面部分是唯一的、不相关部分(以模态方式或通过选项卡栏呈现)的设置。

此方法是为了解决此UITabBarController问题而编写的,因此请将其用作更大问题的部分解决方案。我希望Apple能改进对“多个故事板”的支持。但对于UITabBarController设置,它应该很好用。


很棒的想法。如果我理解正确,那么在-performSegue:withIdentifier:和-prepareForSegue:这两种方法中,我不会看到任何控制器视图方面的区别。 - Szu
@Szu 关于UITabBarController的设置,它应该可以轻松加入而不影响performSegueprepareForSegue。然而,在其他地方使用这种方法时需要注意几点。很有可能prepareForSegue会返回占位符视图控制器,因此通过这种方式传递信息可能会有些困难 :/ - Thomas Verbeek
如果您将来想要使用状态恢复,那么这似乎是一个不好的主意。 - kvn

6
这对于Hawke_Pilot来说有些晚了,但可能会对其他人有所帮助。
从iOS 9.0开始,您可以创建一个关系Segue到另一个故事板。这意味着Tab Bar View Controllers可以链接到另一个故事板上的View Controllers,而无需像其他答案中看到的一些令人费解的技巧。:-)
然而,仅靠这还不够,因为另一个故事板中的接收者不知道它正在连接到一个Tab Bar View Controller,并且不会显示可供编辑的Tab Bar。在将Storyboard Reference指向所需的View Controller后,您需要做的就是选择Storyboard Reference并选择Editor->Embed In->Navigation Controller。这意味着Nav Controller知道它链接到一个Tab Bar View Controller,因为它在同一个故事板上,并将在底部显示Tab Bar并允许编辑按钮图像和标题。不需要编写代码。
诚然,这可能不适合每个人,但对于OP可能有效。

是的!谢谢你。这确实帮了我。 :) - SuperDuperTango

5

如果您不确定是否已经得到了答案,对于其他寻找解决此问题的人,请尝试使用此方法。

在此处输入图片描述

  1. Create the Tab Bar Controller with Navigation Controllers in one storyboard file. And add an empty view controller (I named it RedirectViewController) as shown in the picture.
  2. The child view controller (let's call it SettingsViewController for your case) is located in Settings_iPhone.storyboard.
  3. In RedirectViewController.m, code this:

    - (void)viewWillAppear:(BOOL)animated 
    {
        UIStoryboard *settingsStoryboard = [UIStoryboard storyboardWithName:@"Settings_iPhone" bundle:nil];
        UIViewController *rootSettingsView = [settingsStoryboard instantiateInitialViewController];
    
        [self.navigationController pushViewController:rootSettingsView animated:NO completion:nil];
    }
    
  4. SettingsViewController will be pushed into view instantly when Settings tab is touched.
  5. The solution is not complete yet! You will see "< Back" as the left navigationItem on SettingsViewController. Use the following line in its viewDidLoad method:

    self.navigationItem.hidesBackButton = YES;
    
  6. Also, to prevent the same tab bar item from being tap and causes a jump back to the blank rootViewController, the destination view controllers will need to implement UITabBarControllerDelegate

    - (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController 
    {
        return viewController != tabBarController.selectedViewController;
    }
    

这对我可行。


2
将以下代码添加到LinkViewController中。
-(void) awakeFromNib{
    [super awakeFromNib];
    ///…your custom code here ..

    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:self.storyBoardName bundle:nil];
    UIViewController * scene = nil;

    // Creates the linked scene.
    if ([self.sceneIdentifier length] == 0)
        scene = [storyboard instantiateInitialViewController];
    else
        scene = [storyboard instantiateViewControllerWithIdentifier:self.sceneIdentifier];
        if (self.tabBarController)
            scene.tabBarItem = self.tabBarItem;
    }

这是LinkViewController的屏幕截图:ScreenShot。LinkViewController只是一个占位符,新的viewController将放置在这里。以下是我在我的应用程序中使用的示例代码。 RBStoryboardLink对我非常有帮助。如果您也觉得有用,请告诉我。

那对我没用。这应该放在我的子类化UIViewController中吗? - Hawk_Pilot
如果您想要实现多个故事板,那么在根视图控制器之后必须有一个linkViewController。我会在我的回答中发布屏幕截图。 - Prince Agrawal
请看我上面编辑过的评论以获得更清晰的说明。不确定你的屏幕截图如何与我的两个独立故事板相连。 - Hawk_Pilot
请检查我的答案 @Hawk_Pilot - codercat

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