在iOS中使用选项卡更改容器视图内容

时间:2013-05-02 04:09:49

标签: ios xcode-storyboard container-view

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

view controller

选项卡1 =查看控制器1

标签2 =查看控制器2

选项卡3 =查看控制器3

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

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

我的IBActions也准备好了我的标签:

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

如何在这些IBActions中设置容器以更改容器视图所包含的视图控制器?

在其他一些事情中,这是我尝试过的事情:

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

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

5 个答案:

答案 0 :(得分:16)

使用故事板切换,是否自动布局,某种按钮以及一系列子视图控制器

您希望将容器视图添加到视图中,以及何时切换'子视图控制器按下相应的segue并执行正确的设置工作。

在Storyboard中,您只能将一个Embed Segue连接到Container View。所以你创建了一个中间处理控制器。 make embed segue并为其指定一个标识符,例如EmbededSegueIdentifier。

在父视图中,控制器连接按钮或任何您想要的内容,并在prepare 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。在故事板中,此容器视图控制器不会执行任何与目标子视图控制器相关的操作。

- (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,它不会执行任何操作,使用从Container View Controller调用的相应segue标识符到达每个目标。如果你没有放入空的执行方法,应用程序将崩溃。通常你可以在这里做一些自定义过渡动画。

@implementation SHCDummySegue

@interface SHCDummySegue : UIStoryboardSegue

@end

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

@end

答案 1 :(得分:11)

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

https://github.com/mhaddl/MHCustomTabBarController

答案 2 :(得分:7)

更新:正如您之前发现的那样,推荐使用UITabBarController。如果您想要自定义高度,这是一个良好的开端:My way of customizing UITabBarController's tabbar - Stackoverflow answer

从iOS 5+开始,您可以通过此API自定义外观; UIAppearance Protocol Reference。这是一个很好的教程:How To Customize Tab Bar Background and Appearance

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

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

Embed View Controllers

这些容器“交换”当然可以动画。关于你的自我回答,你可能选择了正确的方法来做到这一点。

答案 3 :(得分:2)

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

UIViewController *selectedViewController;

现在在IBActions中,切换那个AND视图。 e.g。

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

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

到fire视图确实出现了,调用了removeChildViewController,didMoveToParent,addChildViewController,didMoveToParent

答案 4 :(得分:0)

我使用UITabBarController让它工作。为了使用自定义选项卡,我不得不继承TabBarController,并在代码中将按钮添加到控制器。然后,我会在按钮上收听点击事件,并为每个标签设置selectedIndex

这很简单,但是我的故事板中有很多垃圾,就像3个标签一样简单。