13

I got one view controller that contain 1 segmented control and 2 UI views. But I think it's too complicated to update the UI view for enhancement for future editing. I'm using hidden method.

import UIKit

class PopularHistoryViewController: UIViewController {


    @IBOutlet weak var segmentedControl: UISegmentedControl!
    @IBOutlet weak var popularView: UIView!
    @IBOutlet weak var historyView: UIView!

    @IBAction func indexChanged(sender: UISegmentedControl) {
        switch segmentedControl.selectedSegmentIndex
        {
        case 0:
            NSLog("Popular selected")
            //show popular view
            popularView.hidden = false
            historyView.hidden = true
        case 1:
            NSLog("History selected")
            //show history view
            popularView.hidden = true
            historyView.hidden = false
        default:
            break; 
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

}

What I want is 1 container view that contain 2 controller views so I can switch them using segmented control.

enter image description here

halfer
  • 19,824
  • 17
  • 99
  • 186
Nurdin
  • 23,382
  • 43
  • 130
  • 308

3 Answers3

29

The other approach is to only have one child view controller in memory at one time, and then upon changing the selected value in the segmented control, load the new child view controller, transition between one child view controller to the next, and then remove the old child view controller:

let viewControllerIdentifiers = ["first", "second"]  // storyboard identifiers for the child view controllers

@IBAction func didChangeValue(sender: UISegmentedControl) {
    let newController = storyboard!.instantiateViewController(withIdentifier: viewControllerIdentifiers[sender.selectedSegmentIndex])
    let oldController = childViewControllers.last!

    oldController.willMove(toParentViewController: nil)
    addChildViewController(newController)
    newController.view.frame = oldController.view.frame

    transition(from: oldController, to: newController, duration: 0.25, options: .transitionCrossDissolve, animations: {
        // nothing needed here
    }, completion: { _ -> Void in
        oldController.removeFromParentViewController()
        newController.didMove(toParentViewController: self)
    })
}

Obviously, this assumes that you've already got the first child view controller already on the view (easily done if you use the "container view" control in Interface Builder) and the default selected value for the UISegmentedControl matches. You also have to have storyboard identifiers for these two child scenes.

For Swift 2 rendition, see previous revision of this answer.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • 1
    You can also keep both of the view controllers in memory by instantiating them in to properties in `viewDidLoad` or similar - then you can switch between them with state maintained, or even lazy-load on first switch as `UITabBarController` does – Paulw11 Jan 17 '15 at 21:48
  • 1
    Agreed. If Dato' does that, I'd definitely advise the lazy load approach. – Rob Jan 17 '15 at 21:59
  • I got fatal error: unexpectedly found nil while unwrapping an Optional value – Nurdin Jan 18 '15 at 00:06
  • Which line? Have you specified storyboard identifiers for the two child scenes? Do they match what you're using above (e.g. "first", and "second" in my example)? – Rob Jan 18 '15 at 00:08
  • Sorry to bump! I'm having the same error. The line that throws this optional error is the one in which you assign the value to oldController. `let oldController = childViewControllers.last! as UIViewController` – wasimsandhu Jun 12 '16 at 02:00
  • Hello @Rob .i want to know in container view didload calls only one time?please – Krutarth Patel Jul 30 '16 at 06:49
  • Yes, `viewDidLoad` is called when the view is first loaded, and called only once per view controller. Clearly, if you remove the child and let it be deallocated, if you add a new instance again later, then the process starts over. But, the `viewDidLoad` is called once, and only once, per instance. – Rob Jul 30 '16 at 13:35
1

I tried extensively to do this a while ago. For some reason the hidden properties wouldn't work for me, and the container view doesn't seem to update. I know it's not the ideal solution, but I ended up creating two container views and using the segmented control to change the alphas of the container views. Again, not ideal, but it worked nicely.

scott
  • 1,194
  • 7
  • 18
-1
import UIKit

class ContactsView: UIViewController {

    @IBOutlet weak var segmentedControl: UISegmentedControl!
    @IBOutlet weak var privateView: UIView!
    @IBOutlet weak var publicView: UIView!

    @IBAction func segmentChanged(sender: UISegmentedControl) {
        switch segmentedControl.selectedSegmentIndex
        {
        case 0:
            privateView.hidden = false
            publicView.hidden = true
        case 1:
            privateView..hidden = true
            publicView.hidden = false
        default:
            break; 
        }
    }

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

}
Kerberos
  • 4,036
  • 3
  • 36
  • 55
Sai kumar Reddy
  • 1,751
  • 20
  • 23