在iOS中使用多个故事板

78

我的目标是创建一个选项卡应用程序,然后为每个选项卡的视图在单独的storyboard中构建。

Storyboard screenshot

我的主storyboard是一个选项卡视图。

然后我创建了一个带有2个视图控制器的辅助storyboard(storyboard#2)。第一个视图控制器(也被标记为初始)有一个按钮,并且将segue(模态)到第二个视图。 First view controller

我通过子类化和重写来自storyboard#2的loadView来加载视图。 loadView override

这是模拟器输出。

Simulator

当单击“click me”按钮时,会出现EXC_BAD_ACCESS。该segue不起作用,似乎第二个storyboard没有完全加载。

有人之前尝试过这样做并使其正常工作吗?SkillMaster.net有一段YouTube视频,但他没有演示是否在辅助storyboard下工作。 视频在此处:http://youtu.be/D4_twoYvB4M

感谢任何输入和帮助!

截图:

  • http://www.box.com/s/k7foe7gpgh2rs3y8gqxd
  • http://www.box.com/s/rym111x7xqxqao51ruip
  • 这里是两个网址链接,分别为"http://www.box.com/s/k7foe7gpgh2rs3y8gqxd"与"http://www.box.com/s/rym111x7xqxqao51ruip"。

    20
    我不确定这是否是一个好的做法,但我希望:
    1. 不要有一个臃肿的大故事板。
    2. 避免代码难以合并(我们只有少数程序员在工作和提交)。
    - Daddycat Tan Yin See
    也许您可以尝试简单地使用 iOS5 之前我们使用的 .xib 文件?它们应该不难与故事板结合起来。 - Tom van der Woerdt
    我想知道我们是否可以使用Storyboard来做这个。XIB肯定可以工作。 - Daddycat Tan Yin See
    5
    感谢您的信任,我会尽力为您进行翻译。+1000 是针对大型合并冲突友好的 XML 文件分割方案。 - Pierre de LESPINAY
    当您构建产品线而不是单个产品时,使用多个故事板可能是有意义的。在这种情况下,您可以为核心/基础产品创建一个故事板,并为每个定制产品创建一个故事板。 - Kirill Gamazkov
    我尝试通过更好的解决方案来改进它 https://github.com/jeremygrenier/JGLinkedStoryboard - Jeremy Grenier
    9个回答

    82

    这些是我看过的最好的关于多个故事板的文章。

    这位作者不仅告诉你如何在代码中创建新的故事板,

    • 他还推荐了实践中使用多个故事板(更模块化的代码)
    • 讨论了何时使用xib和storyboard(xib保留视图,而storyboard基于控制器)
    • 提供了一个用于连接故事板的class和segue(github上的链接)

    需要注意的是,多个故事板的关键缺点是通常无法使用segue进行连接,但robs库允许稍加调整即可实现。

    此外,还可以参考讨论这里


    44

    楼主编辑了他的问题并包含了答案:

    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"HelpStoryboard" bundle:nil];
    UIViewController* initialHelpView = [storyboard instantiateInitialViewController];
    
    initialHelpView.modalPresentationStyle = UIModalPresentationFormSheet;
    [self presentModalViewController:initialHelpView animated:YES];
    
    当然,这里你调用代码的位置很重要,因为你可能有两个storyboard和它们的视图堆栈在内存中。所以最好从另一个storyboard的视图控制器之外调用这段代码。

    2
    那个问题被从SO上删除了 :-(。无论如何,在这种情况下,您认为您会把这个片段放在哪里?我面临着同样的情况,我真的看不出我应该在哪里连接UITabbarController以实例化每个选项卡的正确故事板。委托tabBarController:shouldSelectViewController:是否合适? - Jean-Denis Muys
    2
    我正在使用上述方法,但不是与选项卡栏一起使用。我将此代码放在由按钮选择器指定的操作方法中。基本上,该代码与我在将segue到场景移动到其自己的故事板之前在prepareForSegue中拥有的代码相同。我最初犯的一个错误是我的新故事板将其主视图嵌入了UINavigationController中 - 在设置它后,我尝试呈现视图而不是导航控制器。 - Rhubarb

    19

    我已经检查了Rhubarb建议的RBStoryboardLink方法。这种实现会替换视图控制器的属性,看起来有些奇怪。我相信我已经找到了避免这种情况的方法。这里有演示项目

    导航控制器

    导航控制器可以将一个引用的视图控制器设置为根视图控制器。实现这样的视图控制器可能看起来像这样:

    @interface ExternNavigationController : UINavigationController
    
    @property (strong, nonatomic) NSString *storyboardName;
    @property (strong, nonatomic) NSString *sceneIdentifier;
    
    @end
    
    @implementation ExternNavigationController
    
    - (void)awakeFromNib
    {
        NSAssert(self.storyboardName, @"storyboardName is required");
    
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:self.storyboardName bundle:nil];
        UIViewController *vc = self.sceneIdentifier
            ? [storyboard instantiateViewControllerWithIdentifier:self.sceneIdentifier]
            : [storyboard instantiateInitialViewController];
    
        self.viewControllers = @[vc];
    }
    
    @end
    

    视图控制器

    当您想要推送在外部故事板中定义的视图控制器时,问题就开始了。这种情况下会复制属性。相反,我们可以实现一个自定义的segue,它将用来自外部故事板的真实目标控制器替换虚假的目标控制器。

    @interface ExternStoryboardSegue : UIStoryboardSegue
    
    @end
    
    @implementation ExternStoryboardSegue
    
    - (id)initWithIdentifier:(NSString *)identifier source:(UIViewController *)source destination:(ExternViewController *)destination
    {
        NSAssert(destination.storyboardName, @"storyboardName is required");
    
        UIStoryboard *storyboard = [UIStoryboard storyboardWithName:destination.storyboardName bundle:nil];
        UIViewController *vc = destination.sceneIdentifier
            ? [storyboard instantiateViewControllerWithIdentifier:destination.sceneIdentifier]
            : [storyboard instantiateInitialViewController];
    
        return [super initWithIdentifier:identifier source:source destination:vc];
    }
    
    - (void)perform
    {
        [[self.sourceViewController navigationController] pushViewController:self.destinationViewController animated:YES];
    }
    
    @end
    

    ExternViewController被用作占位符,其中包含了必要的替换属性(storyboardName和sceneIdentifier)。

    @interface ExternViewController : UIViewController
    
    @property (strong, nonatomic) NSString *storyboardName;
    @property (strong, nonatomic) NSString *sceneIdentifier;
    
    @end
    
    @implementation ExternViewController
    
    @end
    
    我们需要为占位视图控制器设置这些属性和自定义类。并且还要使用ExternStoryboardSegue将视图控制器链接起来。

    IB截图


    1
    我尝试通过更好的解决方案来改进它 https://github.com/jeremygrenier/JGLinkedStoryboard - Jeremy Grenier

    16

    我从一个XIB文件导航到GUI的更复杂部分,为此我使用了一个故事板。因此,我的XIB中的一个按钮将导航到这个故事板。我的代码如下:

    UIStoryboard* storyboard = [UIStoryboard storyboardWithName:@"MyStoryboardIdentifier" bundle:nil];
    UIViewController* myStoryBoardInitialViewController = [storyboard instantiateInitialViewController];
    
    [self.navigationController pushViewController:myStoryBoardInitialViewController animated:YES];
    

    这将成功地将我的Storyboard推到视图中。上面的代码是从按钮的“Touch Up Inside”操作中调用的。


    9

    从Xcode 7开始(并向后移植到iOS8),现在可以使用Storyboard引用。这在WWDC 2015会议中提到过(它在第一小时左右开始讨论)。基本上,你需要做的就是选择要移动到单独Storyboard的ViewController,然后点击Editor->Refactor to Storyboard...。给它一个名称,然后就完成了:

    enter image description here

    请注意,如果您的 VC 已经移动到新的故事板中,并且没有在外部引用(应该是这样的),那么您应该从 main.storyboard 中删除它们的引用(放心,它们仍然会保留在新创建的故事板中,您只是删除了对它们的引用)。

    7

    苹果的文档称您可以拥有多个故事板。不幸的是,他们并没有详细说明如何做到这一点。正如您发现的那样,Interface Builder无法帮助您,因此您必须在代码中完成它。它的工作方式与加载XIB文件类似:

    [UIStoryboard storyboardWithName:@”MyNewStoryboard” bundle:myBundle]
    

    话虽如此,如果您像在评论中提到的那样不想要一个“巨大/臃肿的故事板”,那么XIB确实是正确的选择。这种“庞大”是其优点:所有视图控制器之间的转换都在一个地方布局。拥有多个故事板是为了支持应用程序中多个不同和无关的流程:例如,一个复杂配置流程的故事板和另一个主要用户流程的故事板。


    25
    最大的问题在于当你在一个由三人或以上组成的团队中工作时,合并单个故事板会变成一场噩梦。 - Brian Liang
    2
    我可以想象那是个问题。故事板有很多不完善的地方,你提出的观点又是其中之一。 - Tim
    4
    为了避免合并困扰,你不需要放弃所有故事板提供的便利。只需将不同独立区域隔离,并为每个区域分配单独的故事板。对于所有内部转换,您仍然拥有故事板的所有魔力。选项卡式应用程序非常适合此场景。设置屏幕也是如此。即使您是单独开发人员,拆分故事板为几个独立部分仍然可能很重要。 - KPM

    3

    Xcode 8.2.1

    您可以在外部故事板中引用视图控制器。

    relationship from TabBarController to Storyboard Reference

    UITabBarController拖动连接到外部Storyboard引用,将其添加为“视图控制器”关系。在主Storyboard中,它显示为“Item/square”,但在外部Storyboard中,您应该添加一个UITabBarItem并定义选项卡的名称和图像。

    enter image description here

    当选择“Storyboard Reference”时,属性检查器会显示出来。
    您还需要在外部控制器的故事板中为其赋予“Storyboard ID”(此处未显示),并在引用中引用其名称。

    2
    我发现如果你在第二个故事板中有一个命名的segue,想要在调用performSegueWithIdentifier:时使用它,你必须在源视图控制器的“Identity”标签下设置“Storyboard ID”字段。
    例如,如果你有一个名为“ViewController1”的视图控制器,并且它有一个称为“segue1”的segue指向另一个视图控制器,那么将“ViewController1”的“Storyboard ID”设置为任何值(比如说“viewC1”),然后在ViewController1.m中就可以使用[self performSegueWithIdentifier:@"segue1" sender:self]。

    1

    按以下步骤完成此任务:

    第一步:在组件中搜索Storyboard Reference(参见屏幕截图)

    第二步:选择新的故事板引用(参见屏幕截图)

    第三步:提供新的故事板名称和初始视图控制器的标识符(参见屏幕截图)

    enter image description here

    构建并运行。它会正常工作。

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