I want to add view controller as subview inside the UIView . I have given the required constrains but I am not figure it out why it overlapping . Here is minimal code for it. I have given the top , bottom , leading , trailing constrains but not sure why it overlaps . I want to put svc below the parentView
final class MovieDetailsDisplayViewController: UIViewController {
let movieDetails: MovieDetails
let viewModel: MoviesDetailsViewModel
init(movieDetails: MovieDetails, viewModel: MoviesDetailsViewModel) {
self.movieDetails = movieDetails
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = parentView()
}
override func viewDidLoad() {
super.viewDidLoad()
//let parentView = parentView()
(view as? parentView)?.configure(movieDetails: movieDetails)
childView()
}
private func childView() {
let addHereView = UIView()
addHereView.backgroundColor = .clear
addHereView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(addHereView)
let similarVC = SmiliarMovieViewController(viewModel: viewModel)
addChild(similarVC)
// safely unwrap MySimilarViewController's view
guard let similarView = similarVC.view else { return }
similarView.translatesAutoresizingMaskIntoConstraints = false
addHereView.addSubview(similarView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
addHereView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
addHereView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
addHereView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
// height of the child content
addHereView.heightAnchor.constraint(equalToConstant: 170.0),
similarView.topAnchor.constraint(equalTo: addHereView.topAnchor),
similarView.leadingAnchor.constraint(equalTo: addHereView.leadingAnchor),
similarView.trailingAnchor.constraint(equalTo: addHereView.trailingAnchor),
similarView.bottomAnchor.constraint(equalTo: addHereView.bottomAnchor),
])
similarVC.didMove(toParent: self)
}
private class parentView: UIView {
let scrollView = UIScrollView()
let backdropImageView = UIImageView()
let titleLabel = UILabel()
let overviewLabel = UILabel()
let addHereView = UIView()
private lazy var contentStackView = UIStackView(arrangedSubviews: [backdropImageView, titleLabel, overviewLabel])
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
backgroundColor = .white
addHereView.backgroundColor = .systemBlue
addHereView.translatesAutoresizingMaskIntoConstraints = false
backdropImageView.contentMode = .scaleAspectFill
backdropImageView.clipsToBounds = true
titleLabel.font = UIFont.Heading.medium
titleLabel.textColor = UIColor.Text.charcoal
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping
titleLabel.setContentHuggingPriority(.required, for: .vertical)
overviewLabel.font = UIFont.Body.small
overviewLabel.textColor = UIColor.Text.grey
overviewLabel.numberOfLines = 0
overviewLabel.lineBreakMode = .byWordWrapping
contentStackView.axis = .vertical
contentStackView.spacing = 24
contentStackView.setCustomSpacing(8, after: titleLabel)
setupViewsHierarchy()
setupConstraints()
}
private func setupViewsHierarchy() {
addSubview(scrollView)
scrollView.addSubview(contentStackView)
}
private func setupConstraints() {
scrollView.translatesAutoresizingMaskIntoConstraints = false
backdropImageView.translatesAutoresizingMaskIntoConstraints = false
contentStackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: topAnchor),
scrollView.leadingAnchor.constraint(equalTo: leadingAnchor),
scrollView.bottomAnchor.constraint(equalTo: bottomAnchor),
scrollView.trailingAnchor.constraint(equalTo: trailingAnchor),
backdropImageView.heightAnchor.constraint(equalTo: backdropImageView.widthAnchor, multiplier: 11 / 16, constant: 0),
contentStackView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 24),
contentStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
contentStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
contentStackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: -24)
]
)
scrollView.layoutMargins = UIEdgeInsets(top: 24, left: 16, bottom: 24, right: 16)
preservesSuperviewLayoutMargins = false
}
func configure(movieDetails: MovieDetails) {
backdropImageView.dm_setImage(backdropPath: movieDetails.backdropPath)
titleLabel.text = movieDetails.title
overviewLabel.text = movieDetails.overview
}
}
}
Here is the code for SVC code ..
class SmiliarMovieViewController: UIViewController, UICollectionViewDelegate {
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")
}
lazy var titleLable: UILabel = {
let titleLable = UILabel()
titleLable.translatesAutoresizingMaskIntoConstraints = false
titleLable.font = UIFont.systemFont(ofSize: 24)
titleLable.text = "Similar movies"
return titleLable
}()
private lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let width = 100
layout.itemSize = CGSize(width: width, height: 200)
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.translatesAutoresizingMaskIntoConstraints = false
collection.dataSource = self
collection.delegate = self
collection.register(SimilierMovieCell.self, forCellWithReuseIdentifier: SimilierMovieCell.identifier)
collection.backgroundColor = .clear
return collection
}()
override func viewDidLoad() {
super.viewDidLoad()
setUpUI()
self.viewModel.updatedState = { [weak self] in
DispatchQueue.main.async {
self?.collectionView.reloadData()
}
}
viewModel.fetchSimilarMovie()
}
private func setUpUI() {
view.addSubview(titleLable)
view.addSubview(collectionView)
let safeArea = view.safeAreaLayoutGuide
titleLable.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
titleLable.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 10).isActive = true
titleLable.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: titleLable.bottomAnchor).isActive = true
collectionView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
collectionView.heightAnchor.constraint(equalToConstant: view.frame.width/2).isActive = true
}
}
Here is the cell code ..
class SimilierMovieCell: UICollectionViewCell {
static let identifier = "CompanyCell"
let columnSpacing: CGFloat = 16
let posterSize = CGSize(width: 92, height: 134)
let coverImage = UIImageView()
let tagView = TagView()
let titleLabel = UILabel()
let childStackView = UIStackView()
let containerStackView = UIStackView()
private func commonInit() {
layoutMargins = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
titleLabel.font = UIFont.systemFont(ofSize: 15)
titleLabel.textColor = UIColor.Text.charcoal
titleLabel.numberOfLines = 0
titleLabel.lineBreakMode = .byWordWrapping
coverImage.contentMode = .scaleAspectFit
coverImage.layer.cornerRadius = 8
coverImage.layer.masksToBounds = true
childStackView.spacing = 10
childStackView.alignment = .leading
childStackView.axis = .vertical
containerStackView.spacing = columnSpacing
containerStackView.alignment = .top
containerStackView.translatesAutoresizingMaskIntoConstraints = false
setupViewsHierarchy()
setupConstraints()
}
func setupViewsHierarchy() {
contentView.addSubview(containerStackView)
childStackView.dm_addArrangedSubviews(coverImage)
childStackView.dm_addArrangedSubviews(titleLabel)
childStackView.dm_addArrangedSubviews(tagView)
containerStackView.dm_addArrangedSubviews(childStackView)
}
//Constraints
func setupConstraints() {
NSLayoutConstraint.activate([
containerStackView.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
containerStackView.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
containerStackView.topAnchor.constraint(equalTo: contentView.layoutMarginsGuide.topAnchor),
containerStackView.bottomAnchor.constraint(equalTo: contentView.layoutMarginsGuide.bottomAnchor),
coverImage.widthAnchor.constraint(equalToConstant: posterSize.width),
coverImage.heightAnchor.constraint(equalToConstant: posterSize.height),
titleLabel.topAnchor.constraint(equalTo: coverImage.bottomAnchor),
tagView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor)
])
}
func configure(movie: Movie) {
commonInit()
titleLabel.text = movie.title
tagView.configure(.rating(value: movie.voteAverage))
if let path = movie.posterPath {
coverImage.dm_setImage(posterPath: path)
} else {
coverImage.image = nil
}
}
override func prepareForReuse() {
super.prepareForReuse()
}
}
Really, really quick example of using a child view controller...
We'll start with your "Similar" view controller. A collection view cell and a view controller that creates a collection view:
If you set
MySimilarViewControlleras your Initial View Controller and run the app, it should look like this:Next, we'll setup the "parent" controller, with an image view, a title label and a paragraph label, in a vertical stack view, in a scroll view, with space at the bottom for the "child":
Now, if we set
MyMainViewControlleras the Initial View Controller, we should get this:The image and labels are in the (yellow) scroll view, and we have a blue view where we're going to add the child controller's view.
So, at the end of
viewDidLoad()inMyMainViewController, we'll add this:So, we've added an instance of
MySimilarViewControlleras a child view controller, and we've added itsviewas a subview of the blue view.And we get this:
Edit
If you really, really want to stick with your current approach, couple suggestions...
1 - Set background colors to your UI elements, so you can see the framing.
2 - Learn how to use
Debug View Hierarchyso you can figure out what's going wrong with your layout.After adding (and "faking") the missing information from your post in order to run your code and see the layout, this is what we get:
and if we try to scroll the content up, it only goes this far:
Let's use
Debug View Hierarchyto inspect the layout:You've replaced the controller's built-in view with "parentView" (blue) and you've constrained your scroll view (red) to the full height of the view.
You've then added "similar" controller's view as a subview, but it's on top of the scroll view.
So, in
parentView->setupConstraints(), get rid of the scroll view's bottom anchor:Then, in
childView(), after adding the current constraints, add this:Now, when run, we get this result:
and we can scroll up all the way to the bottom of the paragraph of text:
and our hierarchy now looks like this:
The scroll view no longer extends behind the collection view.