Switching ViewControllers with UISegmentedControl in iOS5

I am trying something very simple but somehow I can’t get it to work. All I try to do is switching between 2 View Controllers using an UISegmentedControl as you can see it for example in the App Store application in the Highlights tab.

I am using iOS5 and Storyboards.

  • iOS program to use multiple UITableView in a single UIViewController
  • Disable/Enable back button from detail view
  • Launching a specific viewController in response to a remote push notification
  • Swift / Enable Editing Mode in View Controller
  • iOS segue freeze for many seconds before showing new view
  • Dismissing a Presented View Controller
  • Here’s my Storyboad line up:

    enter image description here

    So I have a root View Controller and two UITableViews – This 2 TableViews I want to switch.

    Here’s how the implementation file looks like

    #import "SegmentedLocationViewController.h"
    #import "PastEventsLocationViewController.h"
    #import "FutureEventsLocationViewController.h"
    
    @interface SegmentedLocationViewController()
    @property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedControl;
    @property (strong, nonatomic) NSArray *viewControllers;
    @end
    
    
    @implementation SegmentedLocationViewController
    
    @synthesize segmentedControl = _segmentedControl;
    @synthesize viewControllers = _viewControllers;
    
    - (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl*)segmentedControl
    {
        NSLog(@"index: %d", segmentedControl.selectedSegmentIndex);
    }
    
    - (void)setupViewControllers
    {
        PastEventsLocationViewController *pastEventsLocationViewController = [[PastEventsLocationViewController alloc] initWithStyle:UITableViewStylePlain];
        FutureEventsLocationViewController *futureEventsLocationViewController = [[FutureEventsLocationViewController alloc] initWithStyle:UITableViewStylePlain];
    
        self.viewControllers = [NSArray arrayWithObjects:pastEventsLocationViewController, futureEventsLocationViewController, nil];
    }
    
    - (void)setupUI
    {
        [self.segmentedControl addTarget:self action:@selector(indexDidChangeForSegmentedControl:) forControlEvents:UIControlEventValueChanged];
    }
    
    // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        [self setupViewControllers];
        [self setupUI];
    }
    
    - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
        return YES;
    }
    
    @end
    

    I can trigger the switch event and can log the currently selected index. But I don’t have any idea where to go from here.

    Maybe someone can turn my attention towards a certain direction…?

    Thank you very much and have a nice day!

    5 Solutions Collect From Internet About “Switching ViewControllers with UISegmentedControl in iOS5”

    This code works pretty well for your purpose, I use it for one of my new apps.
    It uses the new UIViewController containment APIs that allow UIViewControllers inside your own UIViewControllers without the hassles of manually forwarding stuff like viewDidAppear:

    - (void)viewDidLoad {
        [super viewDidLoad];
        // add viewController so you can switch them later. 
        UIViewController *vc = [self viewControllerForSegmentIndex:self.typeSegmentedControl.selectedSegmentIndex];
        [self addChildViewController:vc];
        vc.view.frame = self.contentView.bounds;
        [self.contentView addSubview:vc.view];
        self.currentViewController = vc;
    }
    - (IBAction)segmentChanged:(UISegmentedControl *)sender {
        UIViewController *vc = [self viewControllerForSegmentIndex:sender.selectedSegmentIndex];
        [self addChildViewController:vc];
        [self transitionFromViewController:self.currentViewController toViewController:vc duration:0.5 options:UIViewAnimationOptionTransitionFlipFromBottom animations:^{
            [self.currentViewController.view removeFromSuperview];
            vc.view.frame = self.contentView.bounds;
            [self.contentView addSubview:vc.view];
        } completion:^(BOOL finished) {
            [vc didMoveToParentViewController:self];
            [self.currentViewController removeFromParentViewController];
            self.currentViewController = vc;
        }];
        self.navigationItem.title = vc.title;
    }
    
    - (UIViewController *)viewControllerForSegmentIndex:(NSInteger)index {
        UIViewController *vc;
        switch (index) {
            case 0:
                vc = [self.storyboard instantiateViewControllerWithIdentifier:@"FooViewController"];
                break;
            case 1:
                vc = [self.storyboard instantiateViewControllerWithIdentifier:@"BarViewController"];
                break;
        }
        return vc;
    }
    

    I got this stuff from chapter 22 of Ray Wenderlichs book iOS5 by tutorial.
    Unfortunately I don’t have a public link to a tutorial. But there is a WWDC 2011 video titled “Implementing UIViewController Containment”

    EDIT

    self.typeSegmentedControl is outlet for your UISegmentedControl

    self.contentView is outlet for your container view

    self.currentViewController is just a property that we’re using to store our currently used UIViewController

    It’s Matthias Bauch solution, but thought of sharing it in Swift anyway!
    Edit:
    Adding a link to a Swift 2.0 ready made demo app.
    https://github.com/ahmed-abdurrahman/taby-segmented-control

    var currentViewController: UIViewController?
    @IBOutlet weak var contentView: UIView!
    @IBOutlet weak var segmentedControl: UISegmentedControl!
    
    @IBAction func switchHappeningTabs(sender: UISegmentedControl) {
        if let vc = viewControllerForSelectedSegmentIndex(sender.selectedSegmentIndex) {
            self.addChildViewController(vc)
            self.transitionFromViewController(self.currentViewController!, toViewController: vc, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromRight, animations: {
                self.currentViewController!.view.removeFromSuperview()
                vc.view.frame = self.contentView.bounds
                self.contentView.addSubview(vc.view)
                }, completion: { finished in
                    vc.didMoveToParentViewController(self)
                    self.currentViewController!.removeFromParentViewController()
                    self.currentViewController = vc
                }
            )        
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        if let vc = self.viewControllerForSelectedSegmentIndex(self.segmentedControl.selectedSegmentIndex) {
            self.addChildViewController(vc)
            self.contentView.addSubview(vc.view)
            self.currentViewController = vc
        }
    }
    
    func viewControllerForSelectedSegmentIndex(index: Int) -> UIViewController? {
        var vc: UIViewController?
        switch index {
        case 0:
                vc = self.storyboard?.instantiateViewControllerWithIdentifier("FooViewController") as? UIViewController
        case 1:
                vc = self.storyboard?.instantiateViewControllerWithIdentifier("BarViewController") as? UIViewController
        default:
            return nil
        }
    
        return vc
    }
    

    For someone want to implement the same in swift

    import UIKit
    
    class HomeController: UIViewController {
    
        var currentViewController:UIViewController?
    
        @IBOutlet weak var homeController: UIView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let initialController:UIViewController = self.viewControllerForSegmentedIndex(0)
    
            self.addChildViewController(initialController)
    
            initialController.view.frame = self.homeController.bounds
            self.homeController.addSubview(initialController.view)
            self.currentViewController = initialController
    
        }
    
        @IBAction func segmentChanged(sender: UISegmentedControl) {
    
            let viewCOntroller:UIViewController = viewControllerForSegmentedIndex(sender.selectedSegmentIndex)
    
            self.addChildViewController(viewCOntroller)
    
            self.transitionFromViewController(self.currentViewController!, toViewController: viewCOntroller, duration: 0.5, options: UIViewAnimationOptions.TransitionFlipFromBottom, animations: {
                self.currentViewController?.view.removeFromSuperview()
                viewCOntroller.view.frame = self.homeController.bounds
                self.homeController.addSubview(viewCOntroller.view)
    
                }, completion:{ finished in
    
                    viewCOntroller.didMoveToParentViewController(self)
                    self.currentViewController?.removeFromParentViewController()
                    self.currentViewController = viewCOntroller
    
            })
    
        }
        func viewControllerForSegmentedIndex(index:Int) -> UIViewController {
            var viewController:UIViewController?
            switch index {
            case 0:
                viewController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdForFirstController")
                break
            case 1:
                viewController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdForSecondController")
                break
            case 2:
                viewController = self.storyboard?.instantiateViewControllerWithIdentifier("StoryboardIdForThirdController")
                break
            default:
                break
            }
            return viewController!
        }
    }
    

    Storyboard view

    enter image description here

    A is root view controller, and B, C is sub view controller.
    When you click segment, add sub view controller.

    result

    result

    design

    design

    code

    import UIKit
    
    class SegmentViewController: UIViewController {
    
        @IBOutlet weak var containerView: UIView!
    
        var leftViewController: LeftViewController!
        var rightViewController: RightViewController!
    
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            if let sb = storyboard {
                leftViewController = sb.instantiateViewControllerWithIdentifier("leftViewController") as! LeftViewController
    
                switchViewController(from: nil, to: leftViewController)
            } else {
                print("storyboard is nil")
            }
        }
    
        func switchViewController(from fromVC: UIViewController?, to toVC: UIViewController?) {
            if let from = fromVC {
                from.willMoveToParentViewController(nil)
                from.view.removeFromSuperview()
                from.removeFromParentViewController()
            } else {
                print("fromVC is nil")
            }
    
            if let to = toVC {
                self.addChildViewController(to)
                to.view.frame = CGRectMake(0, 0, containerView.frame.width, containerView.frame.height)
                self.containerView.insertSubview(to.view, atIndex: 0)
                to.didMoveToParentViewController(self)
            } else {
                print("toVC is nil")
            }
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
    
            removeViewController()
        }
    
        func removeViewController() {
            if let leftVC = leftViewController {
                if let _ = leftVC.parentViewController {
                    print("leftVC is using")
                } else {
                    print("set leftVC = nil")
                    leftViewController = nil
                }
            }
    
            if let rightVC = rightViewController {
                if let _ = rightVC.parentViewController {
                    print("rightVC is using")
                } else {
                    print("set rightVC = nil")
                    rightViewController = nil
                }
            }
        }
    
    
        @IBAction func onSegmentValueChanged(sender: UISegmentedControl) {
            UIView.beginAnimations("xxx", context: nil)
            UIView.setAnimationDuration(0.4)
            UIView.setAnimationCurve(.EaseInOut)
    
            switch sender.selectedSegmentIndex {
            case 0:
                UIView.setAnimationTransition(.FlipFromRight, forView: self.containerView, cache: true)
    
                if let leftVC = leftViewController {
                    switchViewController(from: rightViewController, to: leftVC)
                } else {
                    if let sb = storyboard {
                        leftViewController = sb.instantiateViewControllerWithIdentifier("leftViewController") as! LeftViewController
                        switchViewController(from: rightViewController, to: leftViewController)
                    } else {
                        print("storyboard is nil")
                    }
                }
            default:
                UIView.setAnimationTransition(.FlipFromLeft, forView: self.containerView, cache: true)
    
                if let rightVC = rightViewController {
                    switchViewController(from: leftViewController, to: rightVC)
                } else {
                    if let sb = storyboard {
                        rightViewController = sb.instantiateViewControllerWithIdentifier("rightViewController") as! RightViewController
                        switchViewController(from: leftViewController, to: rightViewController)
                    } else {
                        print("storyboard is nil")
                    }
                }
            }
    
            UIView.commitAnimations()
        }
    }
    

    You could embed your initial view controller in a navigation controller. Do that by selecting the view controller in the storyboard, then Editor->Embed in->Navigation Controller.

    In your indexDidChangeForSegmentedControl: method you simply push the corresponding view controller to the navigation stack:

    - (IBAction)indexDidChangeForSegmentedControl:(UISegmentedControl*)segmentedControl
    {
        [self.navigationController pushViewController:[self.viewControllers objectAtIndex:[segmentedControl.selectedIndex]] animated:YES];
    
    }
    

    But your approach makes not too much sense at all when you are using storyboards.
    I don’t know whether you can wire up a single segmented control button to a view controller using segues. Just try it.