6

I've been going through the documentation and still seem to be on a sticking point.

I have a view controller object C_SelectPhoto. This has a container view. Inside the container view I want the childed view controller, C_SelectPhotoControllerView, to fit inside it. It will just be an array of photos. However, setting the frame and adding the child view controller is not working. If I move the x value of the desired child view controller, no effect happens.

To figure out what is going on I color coded everything. The container, below, is orange. The view the container expects, according to the storyboard is yellow. The view I actually want to fit in there is red.

Here is the storyboard:

enter image description here

Here is my controller code for C_SelectPhoto

class C_SelectPhoto:Controller
{
    @IBOutlet weak var selectPhotoControllerView: UIView!
    var _collectionViewController:C_SelectPhotoControllerView!

    //TODO PERMISSION IS NEEDED BEFORE FETCHING
    func initController()
    {   
        _collectionViewController = Controller.STORYBOARD.instantiateViewControllerWithIdentifier("selectPhotoControllerView") as C_SelectPhotoControllerView
        displayControllerViewController()
    }

    //show the photo selection
    private func displayControllerViewController()
    {
        addChildViewController(_collectionViewController)
        _collectionViewController.view.frame = CGRectMake(100, 0, 500, 500)
        self.view.addSubview(_collectionViewController.view)
        _collectionViewController.didMoveToParentViewController(self)
    }
}

However the result is produces is below: enter image description here

First, the yellow class shouldn't be added at all, I wanted only the red (the UICollectionViewController class). Second, I can tell the red class is being added to the wrong spot because its x value hasn't moved it over at all.

So my question is: How can I add a UIContainerViewController, as a child to the main view controller, C_SelectPhoto, but have the UIContainerViewController frame FIT the container I have in the main view controller?

Thank you!!!

NOTE: The views I am trying to add are UICollectionViewControllers. When I add a UIViewController, the framing works just fine, but as you can see when adding the UICollectionViewControllers, the framing does NOT work, and they are getting added to random offsets and are not respecting my attempts to size them with frame assignments.

Aggressor
  • 13,323
  • 24
  • 103
  • 182

3 Answers3

12

use following Extension for adding childViewController On View

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, view: childController.view)
    childController.didMoveToParentViewController(self)
    childController.willMoveToParentViewController(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])
}}
Phani Sai
  • 1,215
  • 19
  • 33
  • After much frustration with the same problem (specifically controlling the size/location of a child view controller controlling a UICollectionView) I stumbled on this solution. It worked perfectly, including when rotating the device. Up voted. Just one minor update required: ".Top", ".Bottom", ".Left" and ".Right" enumerations have changed at some point to all lower case: ".top", ".bottom",".right" and ".left" – Tomm P Feb 22 '17 at 04:17
  • 1
    You can and should also call `childController.willMoveToParentViewController(self)` – Jordan H Jan 24 '18 at 18:53
  • 3
    2 things - 1, `addChildViewController(_:)` calls `willMove(toParentViewController:)` for you (https://developer.apple.com/library/content/featuredarticles/ViewControllerPGforiPhoneOS/ImplementingaContainerViewController.html). 2, if youre going to call `willSomething()` and `didSomething()`, make sure the `willSomething()` method comes before the `didSomething()` method. – iamthearm Mar 10 '18 at 03:25
11

Updated for Swift 5+

Just one line in your view controller to add child view controller.

Super scalable methods in the extension if you want to add it on any custom view.

 public extension UIViewController {

    /// Adds child view controller to the parent.
    ///
    /// - Parameter child: Child view controller.
    func add(_ child: UIViewController) {
        addChild(child)
        view.addSubview(child.view)
        child.didMove(toParent: self)
    }

    /// It removes the child view controller from the parent.
    func remove() {
        guard parent != nil else {
            return
        }
        willMove(toParent: nil)
        removeFromParent()
        view.removeFromSuperview()
    }
}

How to use:

Adding: In the view controller where you want to add the child view controller.

// let yourChildViewController = Load fro the storyboard or XIB
add(yourChildViewController)

Removing:

yourChildViewController.remove()
Bhuvan Bhatt
  • 3,276
  • 2
  • 18
  • 23
  • Seems lifted from https://www.swiftbysundell.com/basics/child-view-controllers/ without any modifications. At least credit the source. – Abhinav Mathur Jan 04 '23 at 05:16
  • @AbhinavMathur there is very good chance that two approach can be similar. I can see that post was published on 2019 after my answer. And there is no harm in giving credit to swiftysundell if that was the case but I believe here that is not the case. – Bhuvan Bhatt Jan 04 '23 at 13:20
5

If you want the red controller to be the child controller, delete the yellow one, and control-drag from the container to the red controller. There's no need to add it in code, or do any resizing. The red controller will be set to the same size as the container in the storyboard.

rdelmar
  • 103,982
  • 12
  • 207
  • 218
  • I notice the UICollectionView size is NOT respecting the frame I set for it. If I set its frame to (0,0,50,50) when I check its frame in viewDidAppear, its coming out as (0,0,768,960)! If I use a generic UIViewController, this issue does not occur – Aggressor Oct 28 '14 at 03:45
  • @Aggressor, don't set any frame. It should be the same size as the container without any code. Make sure you give the container view whatever constraints it needs to give it the size you want. – rdelmar Oct 28 '14 at 03:47
  • At the bottom of the controller in the storyboard, I see this "Embed segue to 'Controller Name'. Do you know how I can add those anytime I want? I deleted it by accident and I can't seem to re-create it – Aggressor Oct 28 '14 at 03:52
  • @Aggressor, You control drag from the container view to the controller you want to embed, like I said in my answer. – rdelmar Oct 28 '14 at 03:54
  • This describes how I feel right now thanks to you: https://www.youtube.com/watch?v=K4jWvIcL0YU – Aggressor Oct 28 '14 at 04:03
  • http://stackoverflow.com/questions/25404484/add-child-view-controller-swift/35473300#35473300 – Phani Sai Feb 18 '16 at 05:11