Managing Multiple View Controllers with Orientation Control in iOS

Understanding iOS View Controllers and Orientation

Overview of View Controller Hierarchy

In iOS development, a UIViewController is responsible for managing the visual appearance and behavior of its associated view. A typical application consists of multiple view controllers, which are organized in a hierarchical structure. Each view controller has a designated parent-child relationship, where a child view controller inherits properties and behavior from its parent.

The Problem with Fixed Orientation

In this scenario, we have two view controllers: vc1 and vc2. vc1 supports only portrait orientation, while vc2 supports all orientations. We want to achieve the following:

  • vc1 remains in portrait mode when displaying.
  • vc2 can be rotated to landscape mode independently of vc1.

The Solution: Using View Controllers as Containers

One approach to solving this problem is by using a single UIViewController (let’s call it vs1) as the container and two other view controllers (vc1 and vc2) as child view controllers. This setup allows us to manage the orientation of both vc1 and vc2.

Adding Child View Controllers

To add child view controllers, we use the addChildViewController: method in conjunction with addSubview:.

[vs1 addChildViewController:vc1];
[vs1.view addSubview:vc1.view];

[vs1 addChildViewController:vc2];
[vs1.view addSubview:vc2.view];

This code adds both vc1 and vc2 as child view controllers of vs1, which serves as the main container.

Detecting Active View Controller

To detect the currently active view controller, we can use the viewControllers property of the parent view controller (vs1). However, this approach has a limitation: it only returns an array of child view controllers, not the current active one. To resolve this issue, we’ll need to implement our own method to identify the active view controller.

Detecting Active View Controller

We can modify the viewControllers property by overriding the didMoveToParentViewController: method and checking if it’s called for each child view controller.

- (void)addChildViewController:(UIViewController *)controller {
    // ...

    [controller didMoveToParentViewController:self];

    // ...
}

However, this approach still has limitations. We need a more reliable way to detect the active view controller.

Detecting Active View Controller: Using the Window

The most straightforward method is by checking the window property of the parent view controller (vs1). The window contains the root view of the application, which always points to the currently active view controller.

- (void)awakeFromNestingTransition:(id<OS_nestingtransaction>)transition {
    UIViewController *nextVC = [self.nextVC valueForKey:@"nextViewController"];
    // Check if this is the current view controller:
    self = nextVC;
}

In the code above, we override awakeFromNestingTransition:, which gets called when a child view controller (in our case, vc1 and vc2) moves into its parent.

[self.viewcontroller transitionWillBeginWithCompletionHandler:^(void) {
    // Do nothing
}];

When the window changes to our root view controller (vs1), we know that this is the current active view controller. We can use this information to determine which child view controller should be in landscape mode.

Controlling Orientation

To control the orientation of each child view controller, we’ll implement the supportedInterfaceOrientations method for both vc1 and vc2.

- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    // Portrait only
    return UIInterfaceOrientationMaskPortrait;
}

- (BOOL)shouldAutomaticallyAdjustForOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // No, we don't want to adjust for orientation.
    return NO;
}
- (UIInterfaceOrientationMask)supportedInterfaceOrientations {
    // Landscape only
    return UIInterfaceOrientationMaskLandscape;
}

- (BOOL)shouldAutomaticallyAdjustForOrientation:(UIInterfaceOrientation)interfaceOrientation {
    // No, we don't want to adjust for orientation.
    return NO;
}

However, when using these methods, we’re not setting any of our child controllers’ orientations.

@interface Vc1 : UIViewController

@end

@implementation Vc1

- (void) didShow {
    [self setViewOrientation:UIInterfaceOrientationLandscapeRight];
}

// ...

@end

@interface Vc2 : UIViewController

@end

@implementation Vc2

- (void) didShow {
    [self setViewOrientation:UIInterfaceOrientationPortrait];
}

// ...

@end

This way, vc1 will display in landscape orientation and vc2 will display in portrait orientation when they appear on the screen.

Putting it all Together

To sum up our solution: we use a single parent view controller (vs1) as the main container. The child view controllers are added to this container using addChildViewController: and then managed accordingly.

We implement our own method to detect the active view controller by overriding the didMoveToParentViewController: method and checking if it’s called for each child view controller.

For controlling the orientation of each child, we use the supportedInterfaceOrientations method and set their orientations accordingly using setViewOrientation:, in the code that appears in our child controllers.

By following this approach, we’re able to manage the orientation of multiple view controllers independently.


Last modified on 2025-04-13