Change Container View Content with Tabs in iOS

2019-03-08 08:57发布

I'm trying to make a form that spans three tabs. You can see in the screenshot below where the tabs will be. When the user taps a tab, the Container View should update and show a particular view controller I have.

view controller

Tab 1 = View Controller 1

Tab 2 = View Controller 2

Tab 3 = View Controller 3

The view controller shown above has the class PPAddEntryViewController.m. I created an outlet for the Container view within this class and now have a Container View property:

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

I also have my IBActions for my tabs ready:

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

How do I set the container in those IBActions to change the view controller that the Container View holds?

Among a few other things, here's what I've tried:

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

...but it doesn't work. Thanks in advance.

5条回答
Anthone
2楼-- · 2019-03-08 09:00

I got this to work by using a UITabBarController. In order to use custom tabs, I had to subclass the TabBarController and add the buttons to the controller in code. I then listen for tap events on the buttons and set the selectedIndex for each tab.

It was pretty straight forward, but it's a lot of junk in my Storyboard for something as simple as 3 tabs.

查看更多
我命由我不由天
3楼-- · 2019-03-08 09:06

have a member variable to hold the viewController:

UIViewController *selectedViewController;

now in the IBActions, switch that AND the view. e.g.

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

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

to fire view did appear and stuff call removeChildViewController, didMoveToParent, addChildViewController, didMoveToParent

查看更多
贪生不怕死
4楼-- · 2019-03-08 09:12

I recently found the perfect sample code for what I was trying to do. It includes the Storyboard implementation and all the relevant segues and code. It was really helpful.

https://github.com/mhaddl/MHCustomTabBarController

查看更多
疯言疯语
5楼-- · 2019-03-08 09:21

Update: UITabBarController is the recommended way to go, as you found out earlier. In case you'd like to have a custom height, here is a good start: My way of customizing UITabBarController's tabbar - Stackoverflow answer

As of iOS 5+ you have access to customize the appearance via this API; UIAppearance Protocol Reference. Here is a nice tutorial for that: How To Customize Tab Bar Background and Appearance

The most obvious way to achieve what you're looking for is to simply manage 3 different containers (they are simple UIViews) and implement each of them to hold whatever content view you need for each tab (use the hidden property of the containers).

Here is an example of what's possible to achieve with different containers:

Embed View Controllers

These containers "swapping" can be animated of course. About your self-answer, you probably chose the right way to do it.

查看更多
Luminary・发光体
6楼-- · 2019-03-08 09:23

Switching using Storyboard, Auto-layout or not, a Button of some sort, and a series of Child View Controllers

You want to add the container view to your view and when the buttons that 'switch' child view controllers are pressed fire off the appropriate segue and perform the correct setup work.

In the Storyboard you can only connect one Embed Segue to the Container View. So you create an intermediate handling controller. Make the embed segue and give it an identifier, for example EmbededSegueIdentifier.

In your parent view controller wire up the button or whatever you want and keep are reference to your child view controller in the prepare segue. As soon as the parent view controller loads the segue will be fired.

The Parent View Controller

@property (weak, nonatomic) MyContainerViewController *myContainerViewController;

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

It should be fairly easy for you to delegate to your container controller the button presses.

The Container Controller

This next bit of code was partly borrowed from a couple of sources, but the key change is that auto layout is being used as opposed to explicit frames. There is nothing preventing you from simply changing out the lines [self addConstraintsForViewController:] for viewController.view.frame = self.view.bounds. In the Storyboard this Container View Controller doesn't do anything more that segue to the destination child view controllers.

- (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;
    }
}

The Custom Segues

The last thing you need is a custom segue that does nothing, going to each destination with the appropriate segue identifier that is called from the Container View Controller. If you don't put in an empty perform method the app will crash. Normally you could do some custom transition animation here.

@implementation SHCDummySegue

@interface SHCDummySegue : UIStoryboardSegue

@end

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

@end
查看更多
登录 后发表回答