7

I have been trying to figure this out all day and I know it should be able to be done, but being new to iOS development using Objective-C and not Appcelerator I'm having newbie issues.

What I am trying to accomplish is to have an embedded view in one of my other views, but to be able to switch which view is embedded through programming. I am using storyboards. When I tried to use the view container it displays the attached view, but I cannot attach multiple views to the container in the storyboard. I drew up an image, but to my annoyance I cannot post an image on here because I don't have enough "rep" points, so I posted it on my site: http://static.c6gx.com/images/iphone-embedded-views-diagram.jpg

Later on I would like what appears to be a push segue, within that view container, but first I just want to be able to switch between embedded view 1, 2, 3, etc. Am I supposed to be using a view container to accomplish this, or some other type of controller? Then how would I call the transition to change the view?

Thank you for any help you can provide.

Coyote6
  • 332
  • 1
  • 4
  • 12
  • Embed segues are strange, in that they don't work as you expect. You cannot access the view heirarchy of an embedded view controller, so you will have to make a custom segue to do what you want. Optionally, you could have a PageController inside an embed controller, and disable scrolling (then page it manually) – Richard J. Ross III Nov 20 '12 at 23:36
  • Thanks, I will give that a shot and see what I can come up with. – Coyote6 Nov 20 '12 at 23:43
  • @RichardJ.RossIII, "You cannot access the view heirarchy of an embedded view controller" -- yes, you can in fact. If it's a subclassed view controller, you can access the view hierarchy just like any other view controller. If it's not, then you can still get access to the views through the parent's childViewControllers property. – rdelmar Nov 21 '12 at 00:20
  • I don't know if you saw my original post, but I figured out what was wrong with my partially working code, and reposted my answer. This is another possible way to do it along with the way Richard mentioned. – rdelmar Nov 21 '12 at 04:55

3 Answers3

6

You can switch the view controllers like you would do with a container view controller. I made a project where I added a container view (2 actually, but we're only dealing with one here), and added this code to the embedded controller that you get automatically when you drag in the container view. The controller I'm switching to, NewRightController, is a UIViewController subclass -- in the storyboard I set the controller's size to "Freeform", and changed the size of the view to match the size of the embedded controller's view. This doesn't actually affect the size of the view (it still logs as full screen), it just makes it easier to layout the subviews.

-(IBAction)switchToNewRight:(id)sender {
    UIView *rcView = [(ViewController *)self.parentViewController rightView]; // rcView is the right container view in my root view controller that self is embedded in.
    NewRightController *newRight = [self.storyboard instantiateViewControllerWithIdentifier:@"NewRight"];
    newRight.oldRightController = self; // pass self to new controller so I can come back to the same instance
    newRight.view.frame = rcView.bounds;
    [self.parentViewController addChildViewController:newRight];
    [self moveToNewController:newRight];
}

-(void)moveToNewController:(UIViewController *) newController {
    UIView *rcView = [(ViewController *)self.parentViewController rightView];
    [self willMoveToParentViewController:nil];
    [self.parentViewController transitionFromViewController:self toViewController:newController duration:1 options:UIViewAnimationOptionTransitionCurlUp animations:^{}
         completion:^(BOOL finished) {
             [self removeFromParentViewController];
             [rcView constrainViewEqual:newController.view];
             [newController didMoveToParentViewController:self];
         }];
}

I found after much experimentation that the way to get the views to be the right size and to resize properly after rotation was to initially set the frame of the new controller's view to the container view's bounds, but then after the transition animation, add constraints to the new view that keep it sized right after rotation. Since this code might be used over and over, I put it in a category on UIView, with one method, constrainViewEqual:. Here is the code for that:

-(void)constrainViewEqual:(UIView *) view {
    [view setTranslatesAutoresizingMaskIntoConstraints:NO];
    NSLayoutConstraint *con1 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterX relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterX multiplier:1 constant:0];
    NSLayoutConstraint *con2 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeCenterY relatedBy:0 toItem:view attribute:NSLayoutAttributeCenterY multiplier:1 constant:0];
    NSLayoutConstraint *con3 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeWidth relatedBy:0 toItem:view attribute:NSLayoutAttributeWidth multiplier:1 constant:0];
    NSLayoutConstraint *con4 = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:0 toItem:view attribute:NSLayoutAttributeHeight multiplier:1 constant:0];
    NSArray *constraints = @[con1,con2,con3,con4];
    [self addConstraints:constraints];
}
rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • Thanks, I will have to figure your version out at a later date when I get a little more familiar with Objective-C. I'm still a bit confused/overwhelmed by all it at this time. I got the page controller version working, so I am going that route for the time being. I will post my code in an answer so others can view. – Coyote6 Nov 21 '12 at 17:44
1

So after much playing around I got it to work, kind of the way I envisioned. It has no transitions at this point, but I wanted to post the code for others to view.

In the storyboard I have a view controller for the entire game, that has a view container in it and a button. The view embedded into the game view is a page controller that will contain the different type of questions. Then unattached in the storyboard are the different type of question layouts, in my case the teamDisplay and locationDisplay.

In the QuestionViewController, I added two properties to the .h file:

@property (nonatomic, strong) UIPageViewController *pageViewController;
@property (nonatomic, strong) NSMutableArray *questionTypeViewControllers;

Declared a method:

-(void)changeView;

In the .m file, synthesized them:

@synthesize questionTypeViewControllers,
            pageViewController;

In the viewDidLoad method:

pageViewController = [[UIPageViewController alloc]
        initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl
        navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal
        options:nil];

UIViewController *ldvc = [self.storyboard instantiateViewControllerWithIdentifier:@"locationDisplay"];
UIViewController *tdvc = [self.storyboard instantiateViewControllerWithIdentifier:@"teamDisplay"];

questionTypeViewControllers = [NSMutableArray arrayWithObjects:ldvc, tdvc, nil];

NSArray *initView = [NSArray arrayWithObject:ldvc];

[pageViewController setViewControllers:initView
                                      direction:UIPageViewControllerNavigationDirectionForward
                                       animated:NO
                                     completion:nil];
[self addChildViewController:pageViewController];
[self.view addSubview:self.pageViewController.view];

And then implemented the changeView method:

NSArray *initView = [NSArray arrayWithObject: [questionTypeViewControllers objectAtIndex:1]];
[pageViewController setViewControllers:initView
                                 direction:UIPageViewControllerNavigationDirectionForward
                                  animated:NO
                                completion:nil];

In the GameViewController, where the button is located, add an action to the button and call the newly created changeView method from the QuestionViewController.

- (IBAction)change:(id)sender {
    // Need a more reliable way to get the QuestionViewController.
    [[self.childViewControllers objectAtIndex:0] changeView];
}

And that is it. A no brainer right?! ;)

I have no idea if this is the proper way to do this, but it worked. Any suggestions or improvements are welcome.

Coyote6
  • 332
  • 1
  • 4
  • 12
  • I'm not really sure why this works -- you say that you have an embedded pageViewController in your storyboard, but here you're instantiating a new one? Why? You shouldn't have to do that. If you have a page controller embedded in your root view controller, it will be instantiated for you at start up. You can get a reference to it from the controller it's embedded in with the childViewControllers property. – rdelmar Nov 24 '12 at 00:08
1

I wanted to do the same thing, and this is what I found.

Creating Custom Container View Controllers

The gist of it is you create a navigation controller that you embed into the container view, which allows you to navigate through several views as you wish.

Here's a short example/walkthrough:

  1. Add a Navigation Controller to your Storyboard.
  2. Embed the Navigation Controller inside of the Container View.
  3. Select a view to be the root view controller of the navigation controller.
  4. Create manual segues for each view controller that will be used inside of the container view, INCLUDING THE ROOT VIEW CONTROLLER (I almost made the mistake of not adding this one). Make sure you name each segue you create.
  5. In your code, simply call the corresponding manual segue whenever you want to show a different view controller.

Hope this helps!

jpcguy89
  • 217
  • 2
  • 16