16

我正在尝试制作一个跨越三个选项卡的表单。您可以在下面的屏幕截图中看到选项卡的位置。当用户点击选项卡时,容器视图应该更新并显示我拥有的特定视图控制器。

视图控制器

选项卡 1 = 视图控制器 1

选项卡 2 = 视图控制器 2

选项卡 3 = 查看控制器 3

上面显示的视图控制器具有类 PPAddEntryViewController.m。我在这个类中为容器视图创建了一个出口,现在有一个容器视图属性:

@property (weak, nonatomic) IBOutlet UIView *container;

我还为我的标签准备了 IBActions:

- (IBAction)tab1:(id)sender {
  //...
}
- (IBAction)tab2:(id)sender {
  //...
}
- (IBAction)tab3:(id)sender {
  //...
}

如何在这些 IBActions 中设置容器以更改 Container View 拥有的视图控制器?

除其他几件事外,这是我尝试过的:

UIViewController *viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:@"vc1"];
_container.view = viewController1;

...但它不起作用。提前致谢。

4

5 回答 5

16

使用 Storyboard、Auto-layout 与否、某种按钮和一系列子视图控制器进行切换

您希望将容器视图添加到您的视图中,并在按下“切换”子视图控制器的按钮时触发适当的 segue 并执行正确的设置工作。

在 Storyboard 中,您只能将一个 Embed Segue 连接到 Container View。因此,您创建了一个中间处理控制器。制作嵌入 segue 并给它一个标识符,例如 EmbededSegueIdentifier。

在您的父视图控制器中,连接按钮或您想要并保留的任何内容都是在准备 segue 中引用您的子视图控制器。一旦父视图控制器加载,segue 就会被触发。

父视图控制器

@property (weak, nonatomic) MyContainerViewController *myContainerViewController;

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"EmbeddedSegueIdentifier"]) {
        self.myContainerViewController = segue.destinationViewController;
    }
}

您应该很容易将按下的按钮委托给您的容器控制器。

容器控制器

下一段代码部分是从几个来源借来的,但关键的变化是使用了自动布局而不是显式框架。没有什么可以阻止您简单地更改[self addConstraintsForViewController:]. viewController.view.frame = self.view.bounds在 Storyboard 中,这个 Container View Controller 不再做任何与目标子视图控制器相关的事情。

- (void)viewDidLoad
{
    [super viewDidLoad];

    NSLog(@"%s", __PRETTY_FUNCTION__);

    [self performSegueWithIdentifier:@"FirstViewControllerSegue" sender:nil];
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    UIViewController *destinationViewController = segue.destinationViewController;

    if ([self.childViewControllers count] > 0) {
        UIViewController *fromViewController = [self.childViewControllers firstObject];
        [self swapFromViewController:fromViewController toViewController:destinationViewController];
    } else {
        [self initializeChildViewController:destinationViewController];
    }
}

- (void)initializeChildViewController:(UIViewController *)viewController
{
    [self addChildViewController:viewController];
    [self.view addSubview:viewController.view];
    [self addConstraintsForViewController:viewController];

    [viewController didMoveToParentViewController:self];
}

- (void)swapFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
    [fromViewController willMoveToParentViewController:nil];
    [self addChildViewController:toViewController];
    [self transitionFromViewController:fromViewController toViewController:toViewController duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) {
        [self addConstraintsForViewController:toViewController];
        [fromViewController removeFromParentViewController];
        [toViewController didMoveToParentViewController:self];
    }];
}

- (void)addConstraintsForViewController:(UIViewController *)viewController
{
    UIView *containerView = self.view;
    UIView *childView = viewController.view;
    [childView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [containerView addSubview:childView];

    NSDictionary *views = NSDictionaryOfVariableBindings(childView);
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[childView]|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:views]];
    [containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[childView]|"
                                                                          options:0
                                                                          metrics:nil
                                                                            views:views]];
}



#pragma mark - Setters

- (void)setSelectedControl:(ViewControllerSelectionType)selectedControl
{
    _selectedControl = selectedControl;

    switch (self.selectedControl) {
        case kFirstViewController:
            [self performSegueWithIdentifier:@"FirstViewControllerSegue" sender:nil];
            break;
        case kSecondViewController:
            [self performSegueWithIdentifier:@"SecondViewControllerSegue" sender:nil];
            break;
        default:
            break;
    }
}

自定义序列

您需要的最后一件事是一个什么都不做的自定义 segue,使用从容器视图控制器调用的适当的 segue 标识符前往每个目的地。如果您不输入一个空的 perform 方法,应用程序将崩溃。通常你可以在这里做一些自定义的过渡动画。

@implementation SHCDummySegue

@interface SHCDummySegue : UIStoryboardSegue

@end

- (void)perform
{
    // This space intentionally left blank
}

@end
于 2014-01-13T12:31:33.170 回答
11

我最近为我正在尝试做的事情找到了完美的示例代码。它包括 Storyboard 实现和所有相关的 segues 和代码。这真的很有帮助。

https://github.com/mhaddl/MHCustomTabBarController

于 2013-05-10T03:58:15.663 回答
7

更新: UITabBarController 是推荐的方式,正如您之前发现的那样。如果您想要自定义高度,这是一个好的开始:我的自定义 UITabBarController 选项卡栏的方式 - Stackoverflow 答案

从 iOS 5+ 开始,您可以通过此 API 自定义外观;UIAppearance 协议参考。这是一个很好的教程:如何自定义标签栏背景和外观

实现您正在寻找的最明显的方法是简单地管理 3 个不同的容器(它们是简单的 UIView)并实现它们中的每一个来保存每个选项卡所需的任何内容视图(使用容器的隐藏属性)。

以下是使用不同容器可以实现的示例:

嵌入视图控制器

这些容器“交换”当然可以动画化。关于您的自我回答,您可能选择了正确的方法。

于 2013-05-03T06:22:37.447 回答
2

有一个成员变量来保存 viewController:

UIViewController *selectedViewController;

现在在 IBActions 中,切换它和视图。例如

- (IBAction)tab1:(id)sender {
     UIViewController *viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:@"vc1"];

     _container.view = viewController1.view;
     selectedViewController = viewController1;
}

触发视图确实出现了,并且调用 removeChildViewController、didMoveToParent、addChildViewController、didMoveToParent

于 2013-05-03T07:01:25.803 回答
0

我通过使用 UITabBarController 来实现它。为了使用自定义选项卡,我必须继承 TabBarController 并将按钮添加到代码中的控制器。然后我监听按钮上的点击事件并selectedIndex为每个选项卡设置。

这很简单,但是对于像 3 个标签这样简单的东西,我的 Storyboard 中有很多垃圾。

于 2013-05-04T06:20:18.923 回答