【问题标题】:Using multiple storyboards with a TabBarController将多个故事板与 TabBarController 一起使用
【发布时间】:2014-01-23 11:57:37
【问题描述】:

好的,所以在开发我的最新应用程序的过程中,我发现我的storyboard 变得很大,所以为了清理它,我在它失控之前把它分成了multiple storyboards。仅对于设置,我大约有 20 个 tableviewcontrollers 从根 NavigationController 分支出来。 navigationcontroller 是 TabBarController 上的 TabItem,它是 application's 根视图控制器。

我已将TabBar 移动到它自己的StoryBoard 中作为Root_Storyboard,并且导航控制器现在是 Settings_Storyboard 的初始视图。

仅出于测试目的,我在TabBarController (Root_Storyboard) 中放置了几个UIViewControllers 作为选项卡项,并将其中的一个子类化,并将以下代码添加到它的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 中。不知道在这种情况下如何实现下面建议的链接视图。

【问题讨论】:

标签: ios iphone objective-c storyboard uitabbarcontroller


【解决方案1】:

这是很有可能的,也是一个明智的举措 - 整理您的 Storyboard 可以提供更清晰的界面文件供您挖掘,减少 XCode 中的加载时间,以及更好的组编辑。

我已经在 Stack Overflow 上梳理了一段时间,并注意到每个人都在诉诸自定义 Segue 或以编程方式实例化基于选项卡的设置。哎呀。我已经编写了一个简单的 UIViewController 子类,您可以将其用作情节提要的占位符。

代码:

头文件:

#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 数组与 Storyboard 的 初始视图控制器。
  3. 当被请求时,这个子类将返回 Storyboard 的初始视图控制器的 UINavigationItem。这是为了确保加载到 UINavigationBars 中的导航项在交换后与视图控制器相对应。

用法:

要使用它,请将其指定为 Storyboard 中属于 UINavigationController 的 UIViewController 的子类。

为其分配一个恢复 ID,然后就可以开始了。

设置:

以下是您在情节提要中的设置方式:

此设置显示了一个标签栏控制器,其中导航控制器作为其第一个标签控制器。每个导航控制器都有一个简单的 UIViewController 作为其根视图控制器(我已将 UIImageViews 添加到占位符中,以便于记住它链接到的内容)。它们中的每一个都是 TVStoryboardViewController 的子类。每个都有一个恢复 ID 设置到他们应该链接到的情节提要。

这里有一些胜利:

  • 它似乎最适合模态演示,其中子类是导航控制器的根视图控制器。
  • 子类不会将任何控制器压入堆栈 - 它会交换。这意味着您不必手动隐藏后退按钮或覆盖其他地方的选项卡行为。
  • 如果您双击一个选项卡,它会按预期将您带到情节提要的初始视图(您不会再看到该占位符)。
  • 设置超级简单 - 无需自定义转场或设置多个子类。
  • 您可以将 UIImageView 和任何您喜欢的内容添加到占位符视图控制器中,以使您的故事板更清晰 - 它们永远不会显示。

一些限制:

  • 此子类需要属于链中某处的 UINavigationController。
  • 这个子类只会实例化 Storyboard 中的初始视图控制器。如果您想在链的下游进一步实例化视图控制器,您始终可以进一步拆分您的 Storyboard 并重新应用这个子类技巧。
  • 这种方法在推送视图控制器时效果不佳。
  • 这种方法在用作嵌入式视图控制器时效果不佳。
  • 通过 segues 传递消息可能不起作用。这种方法适合界面部分是唯一的、不相关的部分(以模态方式或通过标签栏呈现)的设置。

这个方法被破解来解决这个 UITabBarController 问题,所以用它作为一个更大问题的部分解决方案。我希望 Apple 改进“多故事板”支持。但是,对于 UITabBarController 设置,它应该是一种享受。

【讨论】:

  • 好主意。如果我理解正确,我不会在方法中看到施法者视图控制器的任何区别:-performSegue:withIdentifier: 和 -prepareForSegue:
  • @Szu 关于 UITabBarController 设置,它应该直接插入而不影响performSegue 或prepareForSegue。但是,在其他地方使用这种方法有一些注意事项。 prepareForSegue 很有可能会返回占位符视图控制器,因此通过这种方式传递信息可能很困难:/
  • 如果您想在将来使用状态恢复,这似乎是个坏主意。
【解决方案2】:

这对 Hawke_Pilot 来说有点晚了,但它可能对其他人有所帮助。

从 iOS 9.0 开始,您可以创建关系转场到另一个故事板。这意味着选项卡栏视图控制器可以链接到另一个故事板上的视图控制器,而无需在此处的其他答案中看到一些令人费解的技巧。 :-)

但是,仅此一项并没有帮助,因为另一个故事板中的接收者不知道它被链接到选项卡栏视图控制器并且不会显示选项卡栏进行编辑。将 Storyboard Reference 指向所需的 View Controller 后,您需要做的就是选择 Storyboard Reference 并选择 Editor->Embed In->Navigation Controller。这意味着导航控制器知道它已链接到选项卡栏视图控制器,因为它位于同一个故事板上,并将在底部显示选项卡栏并允许编辑按钮图像和标题。无需代码。

诚然,这可能并不适合所有人,但可能适用于 OP。

【讨论】:

  • 是的!谢谢你。它确实帮助了我。 :)
【解决方案3】:

不确定您的问题是否得到解答,对于正在寻找此问题的解决方案的其他人,请尝试此方法。

  1. 在一个故事板文件中创建带有导航控制器的标签栏控制器。并添加一个空视图控制器(我将其命名为 RedirectViewController),如图所示。
  2. 子视图控制器(在您的情况下我们称之为 SettingsViewController)位于 Settings_iPhone.storyboard。
  3. 在 RedirectViewController.m 中,编写如下代码:

    - (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 将在点击 Settings 选项卡时立即进入视图。
  5. 解决方案还不完整!您将看到“viewDidLoad 方法中使用以下行:

    self.navigationItem.hidesBackButton = YES;
    
  6. 另外,为了防止相同的标签栏项目被点击并导致跳转回空白的 rootViewController,目标视图控制器将需要实现 UITabBarControllerDelegate

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

它对我有用。

【讨论】:

    【解决方案4】:

    将以下代码添加到您的 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 的屏幕截图。

    LinkViewController 只是一个放置新 viewController 的占位符。这是我用于我的应用程序的示例代码。

    RBStoryboardLink 。它对我很有用。如果对您有帮助,请告诉我。

    【讨论】:

    • 这对我不起作用。那应该在我的子类 UIViewController 中吗?
    • 不,如果你想实现多个故事板,实际上你必须在你的 rootViewController 之后有一个 linkViewController。我将在我的答案中发布屏幕截图。
    • 请参阅上面我编辑的评论以获得澄清。不确定您的屏幕截图如何连接到我的 2 个单独的故事板。
    • 检查我的答案@Hawk_Pilot
    猜你喜欢
    • 2016-11-04
    • 1970-01-01
    • 2020-05-28
    • 2015-02-25
    • 2013-03-13
    • 1970-01-01
    • 1970-01-01
    • 2020-08-05
    • 2016-05-31
    相关资源
    最近更新 更多