Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to call addChildViewController

Tags:

ios

I'm trying to have something similar to a UINavigationController so I can customize the animations. To start, I'm just using Apple stock animations. Here's my containerViewController:

- (void)loadView {
    // Set up content view
    CGRect frame = [[UIScreen mainScreen] bounds];
    _containerView = [[UIView alloc] initWithFrame:frame];
    _containerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    self.view = _containerView;

}

- (id)initWithInitialViewController:(UIViewController *)vc {
    self = [super init];
    if (self) {
        _currentViewController = vc;

        [self addChildViewController:_currentViewController];
        [self.view addSubview:_currentViewController.view];
        [self didMoveToParentViewController:self];

        _subViewControllers = [[NSMutableArray alloc] initWithObjects:_currentViewController, nil];
    }
    return self;
}



- (void)pushChildViewController:(UIViewController *)vc animation:(UIViewAnimationOptions)animation {
    vc.view.frame = _containerView.frame;
    [self addChildViewController:vc];

    [self transitionFromViewController:_currentViewController toViewController:vc duration:0.3 options:animation animations:^{

    }completion:^(BOOL finished) {
        [self.view addSubview:vc.view];
        [vc didMoveToParentViewController:self];
        [self.subViewControllers addObject:vc];
    }];
}

- (void)popChildViewController:(UIViewController *)vc WithAnimation:(UIViewAnimationOptions)animation {
    // Check that there is a view controller to pop to
    if ([self.subViewControllers count] <= 0) {
        return;
    }

    NSInteger idx = [self.subViewControllers count] - 1;
    UIViewController *toViewController = [_subViewControllers objectAtIndex:idx];
    [vc willMoveToParentViewController:nil];

    [self transitionFromViewController:vc toViewController:toViewController duration:0.3 options:animation animations:^{

    }completion:^(BOOL finished) {
        [vc.view removeFromSuperview];
        [vc removeFromParentViewController];
        [self didMoveToParentViewController:toViewController];
        [self.subViewControllers removeObjectAtIndex:idx];
    }];
}

I have this ContainerViewcontroller as my rootViewController of the window. I can add my initial viewController and push a view controller. When I try to pop though, I get

ContainerViewController[65240:c07] Unbalanced calls to begin/end appearance transitions for <SecondViewController: 0x8072130>.

I'm wondering what I am doing wrong. I figured my initialViewController is still underneath the secondViewController. Any thoughts? Thanks!

like image 674
Crystal Avatar asked Jan 22 '26 11:01

Crystal


1 Answers

I don't know if this is what's causing your problem, but shouldn't this:

[self didMoveToParentViewController:toViewController];

be:

[toViewController didMoveToParentViewController:self];

Also, I'm not sure what you're doing with the subViewControllers array. It seems to be a duplication of the childViewControllers array that is already a property of a UIViewController.

One other thing I'm not sure is right. In your pop method your toViewController is the last controller in the _subViewControllers array. Don't you want it to be the second to last? Shouldn't the last be the one you're popping? You're popping vc, which is a controller you're passing in to the method, I don't understand that.

This is the way I've made a navigation like controller. In its containment behavior, it acts like a navigation controller, but without a navigation bar, and allows for different transition animations:

@implementation ViewController

-(id)initWithRootViewController:(UIViewController *) rootVC {
    if (self = [super init]) {
        [self addChildViewController:rootVC];
        rootVC.view.frame = self.view.bounds;
        [self.view addSubview:rootVC.view];
    }
    return self;
}


-(void)pushViewController:(UIViewController *) vc animation:(UIViewAnimationOptions)animation {
    vc.view.frame = self.view.bounds;
    [self addChildViewController:vc];
    [self transitionFromViewController:self.childViewControllers[self.childViewControllers.count -2] toViewController:vc duration:1 options:animation animations:nil
            completion:^(BOOL finished) {
            [vc didMoveToParentViewController:self];
            NSLog(@"%@",self.childViewControllers);
    }];
}

-(void)popViewControllerAnimation:(UIViewAnimationOptions)animation {
    [self transitionFromViewController:self.childViewControllers.lastObject toViewController:self.childViewControllers[self.childViewControllers.count -2] duration:1 options:animation animations:nil
                            completion:^(BOOL finished) {
                                [self.childViewControllers.lastObject removeFromParentViewController];
                                NSLog(@"%@",self.childViewControllers);
                            }];
}

-(void)popToRootControllerAnimation:(UIViewAnimationOptions)animation {
    [self transitionFromViewController:self.childViewControllers.lastObject toViewController:self.childViewControllers[0] duration:1 options:animation animations:nil
                            completion:^(BOOL finished) {
                                for (int i = self.childViewControllers.count -1; i>0; i--) {
                                    [self.childViewControllers[i] removeFromParentViewController];
                                }
                                NSLog(@"%@",self.childViewControllers);
                            }];
}

After Edit: I was able to duplicate the back button function with this controller by adding a navigation bar to all my controllers in IB (including in the one that is the custom container controller). I added a bar button to any controllers that will be pushed, and set their titles to nil (I got some glitches if I left the title as "item"). Deleting that title makes the button disappear (in IB) but you can still make connections to it in the scene list. I added an IBOutlet to it, and added this code to get the function I wanted:

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (self.isMovingToParentViewController) {
        self.backButton.title = [self.parentViewController.childViewControllers[self.parentViewController.childViewControllers.count -2] navigationItem].title;
    }else{
        self.backButton.title = [self.parentViewController.childViewControllers[self.parentViewController.childViewControllers.count -3] title];
    }
}

I've shown two different ways that worked to access a title -- in IB you can set a title for the controller which I used in the else clause, or you can use the navigationItem title as I did in the if part of the clause. The "-3" in the else clause is necessary because at the time viewWillAppear is called, the controller that is being popped is still in the childViewControllers array.

like image 65
rdelmar Avatar answered Jan 25 '26 10:01

rdelmar