Add child view controller swift

26,612

Solution 1

When I use your code, run the app, and pause the app, and look at the view hierarchy, I see the following:

(lldb) po [[UIWindow keyWindow] recursiveDescription]
<UIWindow: 0x156bdc30; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x156be750>; layer = <UIWindowLayer: 0x156aa3c0>>
   | <UIView: 0x156c5440; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x156c55c0>>
   |    | <UIScrollView: 0x156c2740; frame = (0 0; 0 568); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x156c4d00>; layer = <CALayer: 0x156c2a80>; contentOffset: {0, 0}; contentSize: {960, 568}>
   |    |    | <UIView: 0x156c6df0; frame = (640 0; 0 536); autoresize = W+H; layer = <CALayer: 0x156c6d80>>
   |    |    | <UIView: 0x156c7100; frame = (320 0; 0 536); autoresize = W+H; layer = <CALayer: 0x156c70a0>>
   |    |    | <UIView: 0x156c73f0; frame = (0 0; 0 536); autoresize = W+H; layer = <CALayer: 0x156c7390>>
   |    |    | <UIImageView: 0x156c8bd0; frame = (0 564.5; 600 3.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x156c8c50>>
   |    |    | <UIImageView: 0x156c9020; frame = (-3.5 32; 3.5 568); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x156c90a0>>
   |    | <_UILayoutGuide: 0x156c5620; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x156c5800>>
   |    | <_UILayoutGuide: 0x156c5c90; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x156c5d10>>

If you're not seeing your subviews there, then likely culprits include:

  • You may not have specified the view controller base class in your scene, and thus this code isn't being run. You can confirm this with a println log statement or breakpoint in side this viewDidLoad and make sure you're hitting this routine.

  • You may not have hooked up the @IBOutlet for the scrollView, and thus scrollView is nil. Confirm this by putting breakpoint in viewDidLoad and examining the scrollView property.


In your revised question, we can now see that the three subviews are present and appear to be the correct size. That's great.

Now the question is why you don't see anything. If you have these defined as scenes in your storyboard, you should:

  • Make sure the "base class" and the "storyboard identifier" is defined for each of these three child scenes; and

  • When your main view controller instantiates the three child view controllers, you would instantiate them from the storyboard using the storyboard identifier (in my example, I used storyboard identifiers of A, B, and C, respectively):

    let Avc = storyboard.instantiateViewControllerWithIdentifier("A") as AViewController
    let Bvc = storyboard.instantiateViewControllerWithIdentifier("B") as BViewController
    let Cvc = storyboard.instantiateViewControllerWithIdentifier("C") as CViewController
    

If you do the above and repeat the recursiveDescription you should see your scene's subviews (e.g. the labels you added) appear in the output.

Solution 2

Use following Extension for adding ChildviewController create file Extensions.swift copy bellow code

import UIKit

extension UIViewController {
    func configureChildViewController(childController: UIViewController, onView: UIView?) {
        var holderView = self.view
        if let onView = onView {
            holderView = onView
        }
        addChildViewController(childController)
        holderView?.addSubview(childController.view)
        constrainViewEqual(holderView: holderView!, view: childController.view)
        childController.didMove(toParentViewController: self)
    }


    func constrainViewEqual(holderView: UIView, view: UIView) {
        view.translatesAutoresizingMaskIntoConstraints = false
        //pin 100 points from the top of the super
        let pinTop = NSLayoutConstraint(item: view, attribute: .top, relatedBy: .equal,
                                    toItem: holderView, attribute: .top, multiplier: 1.0, constant: 0)
        let pinBottom = NSLayoutConstraint(item: view, attribute: .bottom, relatedBy: .equal,
                                       toItem: holderView, attribute: .bottom, multiplier: 1.0, constant: 0)
        let pinLeft = NSLayoutConstraint(item: view, attribute: .left, relatedBy: .equal,
                                     toItem: holderView, attribute: .left, multiplier: 1.0, constant: 0)
        let pinRight = NSLayoutConstraint(item: view, attribute: .right, relatedBy: .equal,
                                      toItem: holderView, attribute: .right, multiplier: 1.0, constant: 0)

        holderView.addConstraints([pinTop, pinBottom, pinLeft, pinRight])
    }
}

User view controller

    import UIKit

    class MyViewControler:UIViewControler  {

    @IBOutlet weak var myView: UIView!



    override func viewDidLoad() {
        super.viewDidLoad()

        let demoViewInstance = storyboard!.instantiateViewController(withIdentifier: "youChiledViewController_Id") as! childViewController
        configureChildViewController(childController: demoViewInstance, onView: myView)

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

}
Share:
26,612

Related videos on Youtube

jmcastel
Author by

jmcastel

Updated on April 02, 2020

Comments

  • jmcastel
    jmcastel about 4 years

    I m trying to add child view controller to a containerViewController

    Child are :

    AViewController
    BViewController
    CViewController
    

    I have no error but when i launch the app, i can swipe the screen, there is 3 section swiped but the A,B,C view controllers don't appeared..

    This is my code, any idea ?

    import UIKit
    
    class ContainerViewController: UIViewController {
    
    @IBOutlet var scrollView: UIScrollView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    
    
        // creat VC
    
        var Avc : AViewController = AViewController ()
        var Bvc : BViewController = BViewController ()
        var Cvc : CViewController = CViewController ()
    
    
    
        // add it to the view hierarchie
    
    
        self.addChildViewController(Cvc)
        self.scrollView.addSubview(Cvc.view)
        Cvc.didMoveToParentViewController(self)
    
        self.addChildViewController(Bvc)
        self.scrollView.addSubview(Bvc.view)
        Bvc.didMoveToParentViewController(self)
    
    
        self.addChildViewController(Avc)
        self.scrollView.addSubview(Avc.view)
        Avc.didMoveToParentViewController(self)
    
        // set the frame
    
        var adminFrame : CGRect = Avc.view.frame
        adminFrame.origin.x = adminFrame.width
        Bvc.view.frame = adminFrame
    
        var BFrame : CGRect = Bvc.view.frame
        BFrame.origin.x = 2*BFrame.width
        Cvc.view.frame = BFrame
    
        // set the frame of the scrollview
    
    
        var scrollWidth: CGFloat = 3*self.view.frame.width
        var scrollHeight: CGFloat = self.view.frame.size.height
        self.scrollView.contentSize = CGSizeMake(scrollWidth, scrollHeight)
    
    
    }
    

    Edit:

    Looking at the view hierarchy, it reports the following:

    <UIWindow: 0x7ff3fad19f70; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x7ff3fac3efe0>; layer = <UIWindowLayer: 0x7ff3fad19740>>
       | <UIView: 0x7ff3fb108b90; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7ff3fb108e60>>
       |    | <UIScrollView: 0x7ff3fac3acf0; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7ff3fb107320>; layer = <CALayer: 0x7ff3fac18e00>; contentOffset: {0, 0}; contentSize: {960, 568}>
       |    |    | <UIView: 0x7ff3fac41ed0; frame = (640 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7ff3fac28a00>>
       |    |    | <UIView: 0x7ff3fac42320; frame = (320 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7ff3fac38e10>>
       |    |    | <UIView: 0x7ff3fac42730; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7ff3fac42810>>
       |    |    | <UIImageView: 0x7ff3faf020f0; frame = (0 564.5; 320 3.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x7ff3fae0df80>>
       |    |    | <UIImageView: 0x7ff3fac1c660; frame = (316.5 0; 3.5 568); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x7ff3fac39420>>
       |    | <_UILayoutGuide: 0x7ff3fb108ec0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x7ff3fb1091e0>>
       |    | <_UILayoutGuide: 0x7ff3fb109c20; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x7ff3fb109d00>>
    
    • Rob
      Rob over 9 years
      You might want to pause the app and at the (lldb) prompt, enter po [[UIWindow keyWindow] recursiveDescription], which will show you the frames for all of the views. It seems to me that you never set Avc.view.frame, so I bet its CGRectZero. I'd suggest Avc.view.frame = self.view.bounds before you start setting the frame of Bvc and Cvc.
    • jmcastel
      jmcastel over 9 years
      I think the only frame witch is dysplayed is the scrollView's frame of my container, i tried your solution but it doesn't work.
    • jmcastel
      jmcastel over 9 years
      The (lldb) po [[UIWindow keyWindow] recursiveDescription] <UIWindow: 0x7ffbd9454b20; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x7ffbd94553d0>; layer = <UIWindowLayer: 0x7ffbd9451c00>> | <UIView: 0x7ffbd9718e60; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7ffbd9719130>> | | <UIScrollView: 0x7ffbd970fdd0; frame = (0 0; 320 568);
    • Rob
      Rob over 9 years
      Brilliant! So your subviews are there! But you haven't apparently added anything to those subviews, so there's nothing to see.
    • jmcastel
      jmcastel over 9 years
      I just changed the color's background for each of them to identify the different subviews, but i can't see the different color when swipping, everything is white. I also add a label...
    • jmcastel
      jmcastel over 9 years
      In fact i m trying to do this github.com/lbrendanl/SwiftSwipeView with this tuto medium.com/swift-programming/…
    • Rob
      Rob over 9 years
      Did you add the label and change the color programmatically or in interface builder. If the latter, you have to instantiate the three child views from the storyboard, as shown in my revised answer. If you're doing it programmatically, update your question with the code you used. And, as always, repeat that recursiveDescription process so you can differentiate between the label not being there vs having a frame that makes it impossible to see (look for zero width or height values).
  • Rob
    Rob over 9 years
    So, when you put your breakpoint in viewDidLoad, and examined scrollView when you hit the breakpoint, it was non-nil?
  • jmcastel
    jmcastel over 9 years
    i ve got an error in this breakpoint with po [[UIWindo keyWindow] recursiveDescription] :error: <EXPR>:1:12: error: expected ',' separator [[UIWindow keyWindow] recursiveDescription] ^ , <EXPR>:1:23: error: expected ',' separator [[UIWindow keyWindow] recursiveDescription]
  • jmcastel
    jmcastel over 9 years
    sorry im noob...i have this <UIScrollView: 0x7fd47ac17540; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7fd47ac1ea90>; layer = <CALayer: 0x7fd47ac16ac0>; contentOffset: {0, 0}; contentSize: {0, 0}>
  • jmcastel
    jmcastel over 9 years
    Ok i tried to instantiate from the storyboard but the ap crash because "doesn't contain a view controller with identifier 'A'". It s normal since i can't set the "A" identifier in the storyboard. In my storyboard i have only one view controler (ContainerViewControler class). A, B, CViewController are xib file so they are not visible ine the storyboard. Thank for your patience and sorry for my bad english..
  • Rob
    Rob over 9 years
    It's weird to use a NIB rather than putting it on your storyboard, but that's fine. If it's in a NIB, you have to instantiate your view controller from that (e.g. let Avc = AViewController(nibName: "nibname", bundle: nil)).
  • jmcastel
    jmcastel over 9 years
    Ok i will trie to do it by putting the views in my storyboard. I just followed the tuto in fatc.
  • jmcastel
    jmcastel over 9 years
    It's working !! yeah !! Thank you so much master Rob !!
  • Natalia
    Natalia almost 8 years
    I find your answer a bit unclear - where do the contstraints come in? Could you clarify a bit what your extension intends to do and why you sugest this approach? It would help me understand how to approach this.
  • Phani Sai
    Phani Sai almost 8 years
    @NataliaChodelski please check above updated code for better understand
  • Takagi
    Takagi almost 6 years
    @PhaniSai I have reworked your code slightly, so there is no explicit unwrapping holderView!. gist.github.com/neoneye/fd01d21944b40fc7794af6c8f0591725