I am trying add two view controller content into single view. Here How I am trying to do it . First create one single view controller and then add the require view into private function then call into viewDidLoad function.
Here is the code..
import UIKit
class CommonViewController: UIViewController {
private let viewModel: MoviesDetailsViewModel
init(viewModel: MoviesDetailsViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
navigationItem.largeTitleDisplayMode = .never
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
combineView()
navigationItem.leftBarButtonItem = UIBarButtonItem.backButton(target: self, action: #selector(didTapBack(_:)))
}
private func combineView() {
let dvc = MovieDetailsViewController(viewModel: viewModel)
let svc = SmiliarMovieViewController(viewModel: viewModel)
view.addSubview(dvc.view)
view.addSubview(svc.view)
}
@objc private func didTapBack(_ sender: UIBarButtonItem) {
navigationController?.popViewController(animated: true)
}
}
I am calling this CommonViewController into didSelectRowAt function here is code for it.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let movie = viewModel.state.movies[indexPath.row]
let viewModel = MoviesDetailsViewModel(movie: movie, apiManager: APIManager())
let com = CommonViewController(viewModel: viewModel)
self.navigationController?.pushViewController(com, animated: true)
}
The problem is when I select the table view cell , I am expecting the show the details of the selected cell but it not rendering the view content. Please note that when I try to call DetailsViewController (specific view controller ) it is able show the content.
Here is the screenshot ..
Error on adding constraints ..
This pattern is called a container view controller, where the current view controller is a “container” for “child” view controllers. You are likely familiar with the hierarchy of views, but there is a similar view controller hierarchy that mirrors that of the views.
So, consider your combineView
:
private func combineView() {
let dvc = MovieDetailsViewController(viewModel: viewModel)
let svc = SmiliarMovieViewController(viewModel: viewModel)
view.addSubview(dvc.view)
view.addSubview(svc.view)
}
You need to call addChild(_:)
to tell the current view controller that it has a child view controller. And when you are done adding the child view controller’s views to the view hierarchy, you have to let it know when you are done doing that by calling didMove(toParent:)
. E.g.:
private func combineView() {
let dvc = MovieDetailsViewController(viewModel: viewModel)
addChild(dvc)
dvc.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(dvc.view)
let svc = SmiliarMovieViewController(viewModel: viewModel)
addChild(svc)
svc.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(svc.view)
NSLayoutConstraint.activate([
dvc.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
dvc.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
dvc.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
svc.view.topAnchor.constraint(equalTo: dvc.view.bottomAnchor),
svc.view.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
svc.view.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
svc.view.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
dvc.view.heightAnchor.constraint(equalTo: svc.view.heightAnchor)
])
dvc.didMove(toParent: self)
svc.didMove(toParent: self)
}
A few observations:
By being children of the current view controller, the current view controller will keep a strong reference to them for you and they’ll be released when the current view controller is dismissed.
By performing the necessary “view controller containment” calls, it ensures that the child view controllers will receive all the necessary lifecycle events (viewDidLoad
, viewWillAppear
, viewDidAppear
, etc.).
Note that the documentation makes reference to willMove(toParent:)
. But you do not need to explicitly call this when adding a child view controller, as addChild(_:)
does this for you.
As noted in the comments, do not forget to specify the layout of the views associated for MovieDetailsViewController
and SmiliarMovieViewController
(e.g., by adding constraints to the current view controller’s view’s anchors).
If all of this feels to cumbersome, it is worth noting that storyboards render all of this code necessary. You can add a “container view” to your storyboard and that does all of this “view controller container” code for you. Feel free to do this programmatically if you would like, but just a FYI.
FWIW, I have posted an example of a container view controller in this GitHub repo, resulting in: