Changing View Controller When Segmented Control Changes

Change views using Segmented Control

Here i have created a complete solution as per your requirement.

Swift 4

//
// SegementedVC.swift
//
// Created by Test User on 01/02/18.
// Copyright © 2018 Test User. All rights reserved.
//

import UIKit

class SegementedVC: UIViewController {

//----------------------------------------------------------------
// MARK:-
// MARK:- Outlets
//----------------------------------------------------------------

@IBOutlet weak var segmentControl : UISegmentedControl!
@IBOutlet weak var containerView : UIView!

//----------------------------------------------------------------
// MARK:-
// MARK:- Variables
//----------------------------------------------------------------

private lazy var firstViewController: FirstViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController") as! FirstViewController

// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)

return viewController
}()

private lazy var secondViewController: SecondViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)

// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController

// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)

return viewController
}()

//----------------------------------------------------------------
// MARK:-
// MARK:- Abstract Method
//----------------------------------------------------------------

static func viewController() -> SegementedVC {
return UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SegementedView") as! SegementedVC
}

//----------------------------------------------------------------
// MARK:-
// MARK:- Memory Management Methods
//----------------------------------------------------------------

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

//----------------------------------------------------------------
// MARK:-
// MARK:- Action Methods
//----------------------------------------------------------------

@IBAction func segmentValueChanged(_ sender: UISegmentedControl) {
updateView()
}

//----------------------------------------------------------------
// MARK:-
// MARK:- Custom Methods
//----------------------------------------------------------------

private func add(asChildViewController viewController: UIViewController) {

// Add Child View Controller
addChildViewController(viewController)

// Add Child View as Subview
containerView.addSubview(viewController.view)

// Configure Child View
viewController.view.frame = containerView.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}

//----------------------------------------------------------------

private func remove(asChildViewController viewController: UIViewController) {
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)

// Remove Child View From Superview
viewController.view.removeFromSuperview()

// Notify Child View Controller
viewController.removeFromParentViewController()
}

//----------------------------------------------------------------

private func updateView() {
if segmentControl.selectedSegmentIndex == 0 {
remove(asChildViewController: secondViewController)
add(asChildViewController: firstViewController)
} else {
remove(asChildViewController: firstViewController)
add(asChildViewController: secondViewController)
}
}

//----------------------------------------------------------------

func setupView() {
updateView()
}

//----------------------------------------------------------------
// MARK:-
// MARK:- View Life Cycle Methods
//----------------------------------------------------------------

override func viewDidLoad() {
super.viewDidLoad()
self.setupView()
}

//----------------------------------------------------------------

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}

//----------------------------------------------------------------

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
}

Storyboard Screenshots

Sample Image

Sample Image

Sample Image

Changing view controller when Segmented Control changes

I'd say it's much simpler to change subviews within a UIViewController, you can set up your subviews in your storyboards and hook them up with IBOulets in your controller you can set the hidden property of your views to YES or NO depending on the control that was clicked.

Now, if you use @Robotic Cat's approach which is also a good solution you can have a little more modularity in how your app works, considering you'd have to place all your logic in one controller using the solution I presented.

How do I change what is shown when the UISegmentedControl index is changed?

You can set tag for each UITextField as below,

let myProduct : UITextField = {
let mp = UITextField(frame: CGRect(x: 10, y: 320, width: 300, height: 10))
mp.placeholder = "Enter Product name"
mp.borderStyle = UITextField.BorderStyle.line
mp.backgroundColor = .white
mp.textColor = .cyan
mp.tag = 11
return mp
}()

let myRetail : UITextField = {
let mp = UITextField(frame: CGRect(x: 10, y: 320, width: 300, height: 20))
mp.placeholder = "Enter Retail price"
mp.borderStyle = UITextField.BorderStyle.line
mp.backgroundColor = .white
mp.textColor = .cyan
mp.tag = 12
return mp
}()

let myExpense : UITextField = {
let mp = UITextField(frame: CGRect(x: 10, y: 320, width: 300, height: 30))
mp.placeholder = "Enter Expense name"
mp.borderStyle = UITextField.BorderStyle.line
mp.backgroundColor = .white
mp.textColor = .cyan
mp.tag = 13
return mp
}()

and then inside handleSegmentChange, remove any view with that tag before adding a new one as below,

//function to handle click of segmented control buttons
@objc fileprivate func handleSegmentChange(sender: UISegmentedControl){
print(segmentedControl.selectedSegmentIndex)

[11, 12, 13].forEach { self.view.viewWithTag($0)?.removeFromSuperview() }

switch segmentedControl.selectedSegmentIndex {
case 0:
view.addSubview(myProduct)
case 1:
view.addSubview(myRetail)
default:
view.addSubview(myExpense)
}
}

For the position issue, you haven't set any frame to UISegmentControl. You can set frame as below to get the result shown,

let segmentedControl: UISegmentedControl = {
let sc = UISegmentedControl(items: ["Inventory", "Sale", "Expense"])
sc.selectedSegmentIndex = 0
sc.layer.cornerRadius = 9
sc.layer.borderWidth = 1
sc.layer.borderColor = UIColor.lightGray.cgColor
sc.layer.masksToBounds = true
sc.frame = CGRect(origin: CGPoint(x: 10, y: 280), size: CGSize(width: 300, height: 30))

sc.addTarget(self, action: #selector(handleSegmentChange), for: .valueChanged)
return sc
}()

Sample Image

Change Segmented Controller properties in SwiftUI

Here is a view that looks like the above and acts like a picker, see if this will work:

struct MinimalPickerView: View {
@State var selected = "Pendiente"
let labels = ["Pendiente", "Atendido", "Cancelado", "Ausente"]

var body: some View {
HStack {
ForEach(labels, id: \.self) { label in
VStack {
Text(label).padding(2)
Rectangle()
.frame(height: 7)
.foregroundColor((selected == label) ? .black : .clear)
}
.onTapGesture {
selected = label
}
.padding(2)
}
}
}

}

Using Segmented Controller to Change Views

Assume you have a ViewController, mainVC, embedded in a UINavigationController, and mainVC has the segmented control. When the first segment is selected you want firstVC viewController presented, and secondVC for the second segment.

I found that if you make mainVC a subclass of UIPageViewController, it does prove difficult to stop the segmented control from animating on/off screen. So instead, I would create a UIPageViewController, and embed it as a subView of the mainVC's view (it can have the same frame). The segmented control is likewise a subview of mainVC's view (so you can lay it out however you like - just below the navigation bar, for example). I did all of this in code in the viewDidLoad of mainVC:

// Set up a page controller to manage the pages....
self.pageController = [[UIPageViewController alloc] initWithTransitionStyle:UIPageViewControllerTransitionStyleScroll navigationOrientation:UIPageViewControllerNavigationOrientationHorizontal options:nil];
// self.pageController.dataSource = self;
// self.pageController.delegate = self;
self.pageController.view.frame = CGRectMake(0,0,self.view.frame.size.width, self.view.frame.size.height);
[self.pageController setViewControllers:@[firstVC] direction:UIPageViewControllerNavigationDirectionForward animated:NO completion:nil];
[self addChildViewController:self.pageController];
[self.view addSubview:self.pageController.view];
// And lay a segmented control over the top....
CGRect segmentFrame = CGRectMake(0,0,self.view.frame.size.width,50); // or whatever
self.segmentedControl = [[UISegmentedControl alloc] initWithItems:@[@"First",@"Second"]];
[self.segmentedControl addTarget:self action:@selector(segmentTapped:) forControlEvents:UIControlEventValueChanged];
self.segmentedControl.frame = segmentFrame;
[self.view addSubview:self.segmentedControl];

In your segmentTapped: method, you can trigger the pageViewController to swap from firstVC to secondVC or vice versa:

[self.pageController setViewControllers:@[secondVC] direction:UIPageViewControllerNavigationDirectionForward animated:YES completion:nil];

If you implement the UIPageViewController's delegate and datasource methods, you can even have swipe gestures to switch pages.

Segmented control to switch collection views

You create a different instance each line

addChild(FirstCVC())
addChild(SecondCVC())
addChild(ThirdCVC())
addChild(FourthCVC())

self.view.addSubview(FirstCVC().view)
self.view.addSubview(SecondCVC().view)
self.view.addSubview(ThirdCVC().view)
self.view.addSubview(FourthCVC().view)

FirstCVC().didMove(toParent: self)
SecondCVC().didMove(toParent: self)
ThirdCVC().didMove(toParent: self)
FourthCVC().didMove(toParent: self)

FirstCVC().view.frame = self.view.bounds
SecondCVC().view.frame = self.view.bounds
ThirdCVC().view.frame = self.view.bounds
FourthCVC().view.frame = self.view.bounds

while it should be

let vc1 = FirstCVC()

let vc2 = SecondCVC()

let vc3 = ThirdCVC()

let vc4 = FourthCVC()

addChild(vc1)
addChild(vc2)
addChild(vc3)
addChild(vc4)

self.view.addSubview(vc1.view)
self.view.addSubview(vc2.view)
self.view.addSubview(vc3.view)
self.view.addSubview(vc4.view)

vc1.didMove(toParent: self)
vc2.didMove(toParent: self)
vc3.didMove(toParent: self)
vc4.didMove(toParent: self)

vc1.view.frame = self.view.bounds
vc2.view.frame = self.view.bounds
vc3.view.frame = self.view.bounds
vc4.view.frame = self.view.bounds

Tip : Also you better create an extension instead of repeating the 4 lines for each vc

How to change segmented control title from another UIViewController (Settings)

To pass the settings back to your first viewController, you can use a closure that is passed from the first viewController.

Here is an example of how to do this. In prepare(for:) set the closure processTipSettings to point to a handler function. In this case, changeSegmentLabels will take the tip settings and make them the labels of the segmented control.

When the user hits save, the @IBAction for the button will call processTipSettings with the values from the textFieldLabels, and changeSegmentLabels will update the titles in the segmented control.


ViewController.swift:

import UIKit

class ViewController: UIViewController {

@IBOutlet var tipsSegmentedController: UISegmentedControl!

func changeSegmentLabels(labels: [String?]) {
if labels.count == 3 {
for i in 0..<3 {
tipsSegmentedController.setTitle(labels[i], forSegmentAt: i)
}
}
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "settings" {
if let dvc = segue.destination as? SettingsViewController {
dvc.processTipSettings = changeSegmentLabels
}
}
}

}

SettingsViewController.swift

class SettingsViewController: UIViewController {

@IBOutlet weak var tip1: UITextField!
@IBOutlet weak var tip2: UITextField!
@IBOutlet weak var tip3: UITextField!

// Call this closure when Save is pressed.
var processTipSettings: (([String?]) -> ())?

@IBAction func saveSettings(_ sender: UIButton) {
processTipSettings?([tip1.text, tip2.text, tip3.text])

_ = self.navigationController?.popViewController(animated: true)
}

}

How do I use a UISegmentedControl to switch views?

The simplest approach is to have two views that you can toggle their visibility to indicate which view has been selected. Here is some sample code on how it can be done, definitely not an optimized way to handle the views but just to demonstrate how you can use the UISegmentControl to toggle the visible view:



- (IBAction)segmentSwitch:(id)sender {
UISegmentedControl *segmentedControl = (UISegmentedControl *) sender;
NSInteger selectedSegment = segmentedControl.selectedSegmentIndex;

if (selectedSegment == 0) {
//toggle the correct view to be visible
[firstView setHidden:NO];
[secondView setHidden:YES];
}
else{
//toggle the correct view to be visible
[firstView setHidden:YES];
[secondView setHidden:NO];
}
}



You can of course further re-factor the code to hide/show the right view.



Related Topics



Leave a reply



Submit