Add Child View Controller to Current View Controller

Add child view controller to current view controller

didMoveToParentViewController must be the last.

Present child view controller

The view containment calls are incorrect. (See below.)

But the “Unbalanced calls” error message suggests that there might be some other, deeper problem elsewhere in your code base. The incorrect view controller containment calls are insufficient to manifest this error.

One generally gets this error when attempting to initiate a transition while another is underway (e.g., trying to present/dismiss view controllers inside the viewDidLoad, viewWillAppear or viewWillDisappear methods).

But the supplied code snippet is insufficient to manifest the problem you describe. We need MCVE. I would suggest you create a blank project and figure out what you need to add to it in order to manifest your error.


That having been said, the correct view controller containment calls to remove a child are willMoveToParentViewController, followed by removeFromSuperview, followed by removeFromParentViewController, e.g.:

[self willMoveToParentViewController:nil];
[self.view removeFromSuperview];
[self removeFromParentViewController];

Note, I did not call didMoveToParentViewController, because, as the documentation says:

The removeFromParentViewController method automatically calls the didMoveToParentViewController: method of the child view controller after it removes the child.


Obviously, when adding a child, the converse is true, that you do not call willMoveToParentViewController, but you do call didMoveToParentViewController:

[self addChildViewController:child];
[self.view addSubview:child.view];
child.view.frame = ...;
[self didMoveToParentViewController:self];

Again, the documentation advises us:

When your custom container calls the addChildViewController: method, it automatically calls the willMoveToParentViewController: method of the view controller to be added as a child before adding it.

iOS : Why do we need to add child view controller when adding view as subview does the work?

Certainly viewDidLoad was called. That happened instantly as soon as you referred to ViewController2's view in your code.

But let's say your ViewController2 does other things besides load a view. Suppose its view contains a button that is hooked through an action to a function in ViewController2. If you now tap that button nothing happens.

That's because the ViewController2 itself is dead: it has vanished in a puff of smoke.

You can see that by implementing deinit in ViewController2. You will see that, just as viewDidLoad is called, so is deinit. You are left with a view controller's view that has no view controller. That is bad.

There is a view controller hierarchy that is responsible for maintaining relations between view controllers. When you add ViewController2 as a child view controller of ViewController1, you maintain that hierarchy, and you maintain it correctly according to the rules, which say:

If VC2's view is somewhere inside VC1's view, then VC2 needs to be a child (at some depth) of VC1.

In other words, the view hierarchy and the view controller hierarchy must run together. Otherwise, the responder chain will break and life will become chaos.


(There are other requirements when you make one view controller the child of another, like sending didMoveToParent to the child as part of the opening dance, along with other message forwarding responsibilities later, so as to ensure that the child view controller gets other messages like viewDidAppear at the right time. It's a complex business. However, I've focussed my answer on the very basic part of what you asked.)


I should add: if your goal was merely to fetch a view out of a nib and stuff it into your own view, you can certainly do that, no problem. What you must not do is use a view controller as a kind of magnet or vacuum cleaner to fetch a view for you if your intention is then to let go of the view controller itself.

Add/remove child view controller using UIViewController extension

You need to update your method like this:

func add(_ child: UIViewController, frame: CGRect? = nil) {
addChild(child)
//child.view.frame = self.view.frame
if frame != nil {
if let frame = frame {
child.view.frame = frame
}
} else {
child.view.frame = self.view.frame
}
view.addSubview(child.view)
child.didMove(toParent: self)
}

func remove(_ child: UIViewController) {
child.view.removeFromSuperview()
willMove(toParent: nil)
removeFromParent()
}

Child View Controller and Margin

You don't give the child view neither a frame nor constraints

child.view.translatesAutoresizingMaskIntoConstraints = false 

Also you should add view not imageView

view.addSubview(child.view) 
NSLayoutConstraint.activate([
child.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
child.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 8),
child.view.leftAnchor.constraint(equalTo: view.leftAnchor),
child.view.rightAnchor.constraint(equalTo: view.rightAnchor),
])

and set

navigationController?.navigationBar.prefersLargeTitles = false

Remove child view controller

This is to make it modular.

To remove a single child:

func removeViewController(_ viewController: UIViewController) {
// this is to notify the the child that it's about to be removed
viewController.willMove(toParent: nil)
// this is to remove the child's view from its superview
viewController.view.removeFromSuperview()
// this is to remove the child vc from its parent vc
viewController.removeFromParent()
}

To remove all children:

func removeAllChildren() {
if self.children.count > 0 {
let childrenVC: [UIViewController] = self.children
for chlidVC in childrenVC {
chlidVC.willMove(toParent: nil)
chlidVC.view.removeFromSuperview()
chlidVC.removeFromParent()
}
}
}

What is the difference between presenting a ViewController vs adding a ViewController as a child

The difference is that when you present a controller, it becomes modal, and when you add it as a child controller, it doesn't. The difference is in semantics and use cases.

Modal

As an example of modal window, imagine a window with an OK button in macOS that prevents you from interacting with the parent window until you close it.

Hence why there can be only one view controller presented by a particular controller: the presented controller becomes “main” for user interaction, and the user must close it to return to the parent window.

A typical example of a modal window is an alert.

Child

But a child view controller is a different story. There can be any amount of them on a particular VC.

The parent controller becomes sort of container for child view controllers. For example, there can be a dashboard with multiple panels, and each panel can be a separate controller. This allows to separate logic between different pieces of an application instead of cramming everything into a single controller, to implement user customization in easy way, etc.

A typical example of a parent-child controller interaction is UINavigationController: the navigation controller is the parent, and all view controllers pushed into it are its children. We don't make an uber controller with the whole application's logic, we separate it between different screens, and the navigation controller is only responsible for presentation.

Present View Controller and Add Child View Controller Cause App Freeze

This may be a better structure to take. When you switch from A content to B content, you can move / size the elements all you want... And since they will be separate subviews of your "main" view, they can overlap if your interface would benefit from that layout:

Sample Image



Related Topics



Leave a reply



Submit