简体   繁体   English

使用带有TabBarController的多个故事板

[英]Using multiple storyboards with a TabBarController

Okay, so in the process of developing my newest app, I found that my storyboard got huge, so in an effort to clean it up some, i have divided it into multiple storyboards before it gets out of hand. 好吧,所以在开发我的最新应用程序的过程中,我发现我的storyboard变得庞大,所以为了清理它,我把它分成multiple storyboards然后才失控。 just for settings alone i have roughly 20 tableviewcontrollers that branch out from a root NavigationController . 仅仅为了设置我有大约20个tableviewcontrollers从根NavigationController分支出来。 That navigationcontroller was a TabItem on a TabBarController , which is the application's root view controller. 那个navigationcontrollerTabBarController上的一个TabItem,它是application's根视图控制器。

I've moved the TabBar into it's own StoryBoard as the Root_Storyboard and the Navigation controller is now the initial view of the Settings_Storyboard. 我已将TabBar移动到它自己的StoryBoard作为Root_Storyboard ,导航控制器现在是Settings_Storyboard的初始视图。

Just for testing purposes, I placed a few UIViewControllers as tab items in the TabBarController (Root_Storyboard) and subclassed one and added the following code to it's viewWillAppear method. 仅出于测试目的,我在TabBarController (Root_Storyboard)中放置了一些UIViewControllers作为选项卡项,并将其子类化为一个,并将以下代码添加到它的viewWillAppear方法中。 It works great, but I know that the presentViewController displays the NavigationController modally and hides the tabBar . 它工作得很好,但我知道presentViewController以模态presentViewController显示NavigationController并隐藏tabBar Obviously I don't want that, how do I get it to push properly so that the TabBar remains visible? 显然我不想那样,我怎样才能让它正确推动以便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];
}

Edit - To clarify. 编辑 - 澄清。 The above code is the subclassed method for a UIViewController (child of UITabBarController:index(1)) in the Root_iPhone.storyboard. 上面的代码是Root_iPhone.storyboard中UIViewController (child of UITabBarController:index(1))的子类方法。 The UINavigationController/UITableViewController that I am trying to load is found in Settings_iPhone.storyboard . 我试图加载的UINavigationController/UITableViewController可以在Settings_iPhone.storyboard找到。 Not sure how to implement the linkView suggested below in this situation. 在这种情况下,不确定如何实现下面建议的linkView。

This is quite possible and a smart move - decluttering your Storyboards presents cleaner interface files to dig through, reduced loading times in XCode, and better group editing. 这是非常可能的,一个聪明的举动 - 整理你的故事板提供了更清晰的界面文件,可以深入挖掘,减少XCode的加载时间,以及更好的组编辑。

I've been combing across Stack Overflow for a while and noticed everyone is resorting to Custom Segues or instantiating tab based setups programmatically. 我已经在Stack Overflow上梳理了一段时间,并注意到每个人都采用自定义分段或以编程方式实例化基于选项卡的设置。 Yikes. 让人惊讶。 I've hacked together a simple UIViewController subclass that you can use as a placeholder for your storyboards. 我已经破解了一个简单的UIViewController子类,您可以将其用作故事板的占位符。

Code: 码:

Header file: 头文件:

#import <UIKit/UIKit.h>

@interface TVStoryboardViewController : UIViewController

@end

Implementation file: 实施文件:

#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

Description: 描述:

  1. The view controller uses its Restoration Identifier to instantiate a storyboard in your project. 视图控制器使用其恢复标识符来实例化项目中的故事板。
  2. Once loaded, it will attempt to replace itself in its UINavigationController's viewController array with the Storyboard's initial view controller. 加载后,它将尝试使用Storyboard的初始视图控制器在其UINavigationController的viewController数组中替换自身。
  3. When requested, this subclass will return the UINavigationItem of the Storyboard's initial view controller. 请求时,此子类将返回Storyboard的初始视图控制器的UINavigationItem。 This is to ensure that navigation items loaded into UINavigationBars will correspond to the view controllers after the swap. 这是为了确保加载到UINavigationBars中的导航项与交换后的视图控制器相对应。

Usage: 用法:

To use it, assign it as the subclass of a UIViewController in your Storyboard that belongs to a UINavigationController. 要使用它,请将它指定为属于UINavigationController的Storyboard中的UIViewController的子类。

在此输入图像描述

Assign it a Restoration ID, and you're good to go. 为它分配一个恢复ID,你很高兴。

在此输入图像描述

Setup: 建立:

And here's how you set it up in the Storyboard: 以下是您在Storyboard中设置的方式:

图显示设置

This setup shows a tab bar controller with navigation controllers as its first tab controllers. 此设置显示标签栏控制器,其中导航控制器作为其第一个标签控制器。 Each navigation controller has a simple UIViewController as its root view controller (I've added UIImageViews to the placeholders to make it easy to remember what it links to). 每个导航控制器都有一个简单的UIViewController作为其根视图控制器(我已经将UIImageViews添加到占位符中,以便于记住它链接到的内容)。 Each of them is a subclass of TVStoryboardViewController. 它们中的每一个都是TVStoryboardViewController的子类。 Each has a Restoration ID set to the storyboard they should link to. 每个都有一个恢复ID设置为他们应链接到的故事板。

Some wins here: 有些胜利:

  • It seems to work best for modal presentations where the subclass is the root view controller of a navigation controller. 它似乎最适用于模态演示,其中子类是导航控制器的根视图控制器。
  • The subclass doesn't push any controllers on the stack - it swaps. 子类不会推送堆栈上的任何控制器 - 它交换。 This means you don't have to manually hide a back button or override tab behaviour elsewhere. 这意味着您不必手动隐藏后退按钮或覆盖其他位置的选项卡行为。
  • If you double tap on a tab, it will take you to the Storyboard's initial view, as expected (you won't see that placeholder again). 如果您双击选项卡,它将按照预期将您带到Storyboard的初始视图(您将不会再次看到该占位符)。
  • Super simple to set up - no custom segues or setting multiple subclasses. 设置超级简单 - 没有自定义segues或设置多个子类。
  • You can add UIImageViews and whatever you like to the placeholder view controllers to make your Storyboards clearer - they will never be shown. 您可以将UIImageViews和您喜欢的任何内容添加到占位符视图控制器中,以使您的故事板更清晰 - 它们永远不会显示。

Some limitations: 一些限制:

  • This subclass needs to belong to a UINavigationController somewhere in the chain. 该子类需要属于链中某处的UINavigationController。
  • This subclass will only instantiate the initial view controller in the Storyboard. 此子类仅实例化Storyboard中的初始视图控制器。 If you want to instantiate a view controller further down the chain, you can always split your Storyboards further and reapply this subclass trick. 如果要在链中进一步实例化视图控制器,可以始终进一步拆分故事板并重新应用此子类技巧。
  • This approach doesn't work well when pushing view controllers. 推送视图控制器时,此方法无法正常工作。
  • This approach doesn't work well when used as an embedded view controller. 当用作嵌入式视图控制器时,此方法不能很好地工作。
  • Message passing via segues likely won't work. 通过segue传递的消息可能不起作用。 This approach suits setups where sections of interface are unique, unrelated sections (presented modally or via tab bar). 这种方法适合于界面部分是唯一的,不相关的部分(以模态或通过标签栏呈现)的设置。

This approach was hacked up to solve this UITabBarController problem, so use it as a partial solution to a bigger issue. 这种方法被黑客攻击以解决这个UITabBarController问题,因此将其用作更大问题的部分解决方案。 I hope Apple improves on 'multiple storyboard' support. 我希望Apple能够改进“多故事板”支持。 For the UITabBarController setup however, it should work a treat. 但是对于UITabBarController设置,它应该是一种享受。

This is a bit late for Hawke_Pilot but it might help others. 对于Hawke_Pilot来说这有点晚了,但它可能对其他人有所帮助。

From iOS 9.0 onwards you can create a Relationship Segue to another storyboard. 从iOS 9.0开始,您可以创建一个关系Segue到另一个故事板。 This means that Tab Bar View Controllers can link to View Controllers on another storyboard without some of the mind-bending tricks seen in other answers here. 这意味着标签栏视图控制器可以链接到另一个故事板上的视图控制器,而不会在其他答案中看到一些令人费解的技巧。 :-) :-)

However, this alone doesn't help because the recipient in the other storyboard doesn't know it's being linked to a Tab Bar View Controller and won't display the Tab Bar for editing. 但是,仅此一点无济于事,因为其他故事板中的收件人不知道它被链接到选项卡栏视图控制器,并且不会显示选项卡栏进行编辑。 All you need to do once you point the Storyboard Reference to the required View Controller is select the Storyboard Reference and choose Editor->Embed In->Navigation Controller. 将Storyboard参考指向所需的View Controller后,您只需选择Storyboard Reference并选择Editor-> Embed In-> Navigation Controller。 This means that the Nav Controller knows it's linked to a Tab Bar View Controller because it's on the same storyboard and will display the Tab Bar at the bottom and allow editing of the button image and title. 这意味着导航控制器知道它链接到标签栏视图控制器,因为它位于同一个故事板上,并将在底部显示标签栏并允许编辑按钮图像和标题。 No code required. 无需代码。

Admittedly, this may not suit everyone but may work for the OP. 不可否认,这可能不适合所有人,但可能会为OP工作。

Not sure if your question is answered, and for others looking for a solution to this problem, try this method. 不确定您的问题是否得到解答,并且对于寻找此问题的解决方案的其他人,请尝试此方法。

在此输入图像描述

  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. 并添加一个空视图控制器(我将其命名为RedirectViewController),如图所示。
  2. The child view controller (let's call it SettingsViewController for your case) is located in Settings_iPhone.storyboard. 子视图控制器(我们称之为SettingsViewController为您的案例)位于Settings_iPhone.storyboard中。
  3. In RedirectViewController.m, code this: 在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 will be pushed into view instantly when Settings tab is touched. 触摸“设置”选项卡后,SettingsViewController将立即进入视图。
  5. The solution is not complete yet! 解决方案尚未完成! You will see "< Back" as the left navigationItem on SettingsViewController. 您将在SettingsViewController上看到“<Back”作为左侧navigationItem。 Use the following line in its viewDidLoad method: viewDidLoad方法中使用以下行:

     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 此外,为了防止相同的标签栏项被轻击并导致跳回到空白的rootViewController,目标视图控制器将需要实现UITabBarControllerDelegate

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

It works for me. 这个对我有用。

Add Following code to your LinkViewController 将以下代码添加到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;
    }

Here is the screenShot for LinkViewController 这是LinkViewController的screenShot 截图 .

LinkViewController is just a placeholder where new viewController would be placed. LinkViewController只是一个占位符,可以放置新的viewController。 Here is the sample code which I used for my app. 这是我用于我的应用程序的示例代码。

RBStoryboardLink . RBStoryboardLink Its working great for me. 它对我很有用。 Let me know if it is helpful for you. 如果它对您有帮助,请告诉我。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM