I have two UIView and I want to add detailsView as subview with other view. I am getting following error:
Cannot convert value of type 'MovieDetailsDisplayViewController.detailsView.Type' to expected argument type 'MovieDetailsDisplayViewController.View'
View model code ..
enum MoviesDetailsViewModelState {
case loading(Movie)
case loaded(MovieDetails)
case pageLoaded(Page<Movie>)
case error
var title: String? {
switch self {
case .loaded(let movie):
return movie.title
case .loading(let movie):
return movie.title
case .error:
return nil
case .pageLoaded:
return nil
}
}
var movie: MovieDetails? {
switch self {
case .loaded(let movie):
return movie
case .loading, .error:
return nil
case .pageLoaded:
return nil
}
}
var page: Page<Movie>? {
switch self {
case .loading, .error, .loaded:
return nil
case .pageLoaded(let page):
return page
}
}
}
final class MoviesDetailsViewModel {
private let apiManager: APIManaging
private let initialMovie: Movie
var moviePage = [Movie]()
init(movie: Movie, apiManager: APIManaging = APIManager()) {
self.initialMovie = movie
self.apiManager = apiManager
self.state = .loading(movie)
}
var updatedState: (() -> Void)?
var state: MoviesDetailsViewModelState {
didSet {
updatedState?()
}
}
func fetchData() {
apiManager.execute(MovieDetails.details(for: initialMovie)) { [weak self] result in
guard let self = self else { return }
switch result {
case .success(let movieDetails):
self.state = .loaded(movieDetails)
case .failure:
self.state = .error
}
}
}
func fetchSimilarMovie() {
apiManager.execute(Movie.similiar(for: initialMovie.id)) { [weak self] result in
guard let self = self else { return }
switch result {
case.success(let page):
self.state = .pageLoaded(page)
self.moviePage = page.results
print(moviePage)
case .failure(let error):
self.state = .error
print(error)
}
}
}
}
Here is the code for both views:
final class MovieDetailsDisplayViewController: UIViewController {
let movieDetails: MovieDetails
init(movieDetails: MovieDetails) {
self.movieDetails = movieDetails
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
view = ParentView()
view = DetailsView(coder: <#NSCoder#>)
}
override func viewDidLoad() {
super.viewDidLoad()
(view as? ParentView)?.configure(movieDetails: movieDetails)
view.addSubview(DetailsView)
}
private class ParentView: UIView {
let scrollView = UIScrollView()
let backdropImageView = UIImageView()
let titleLabel = UILabel()
let overviewLabel = UILabel()
let similarLabel = UILabel()
private lazy var contentStackView = UIStackView(arrangedSubviews: [backdropImageView, titleLabel, overviewLabel, similarLabel])
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
backgroundColor = .white
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
similarLabel.font = UIFont.Body.smallSemiBold
similarLabel.textColor = UIColor.Text.charcoal
similarLabel.numberOfLines = 0
similarLabel.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
}
}
private class DetailsView: UIView, UICollectionViewDataSource, UICollectionViewDelegate {
let viewModel: MoviesDetailsViewModel
init(viewModel: MoviesDetailsViewModel) {
self.viewModel = viewModel
super.init()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
fileprivate let collectionView:UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.translatesAutoresizingMaskIntoConstraints = false
cv.register(SimilierMovieCell.self, forCellWithReuseIdentifier: "CompanyCell")
cv.backgroundColor = .lightGray
return cv
}()
private func setUpUI() {
addSubview(collectionView)
//Add constraint
collectionView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
collectionView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
collectionView.topAnchor.constraint(equalTo: topAnchor).isActive = true
collectionView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: collectionView.frame.width/2.5, height: collectionView.frame.width/2)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
let items = viewModel.moviePage.count
return items
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SimilierMovieCell.identifier, for: indexPath) as? SimilierMovieCell
let listMovie = viewModel.moviePage[indexPath.row]
print(listMovie)
cell?.configure(listMovie)
return cell ?? SimilierMovieCell()
}
}
}
Here is the screenshot of the error.
You report this error:
Cannot convert value of type 'MovieDetailsDisplayViewController.detailsView.Type' to expected argument type 'MovieDetailsDisplayViewController.View'
That is a result of the line that says:
view.addSubview(DetailsView)
DetailsView
is the name of a type. But you need to set it to be an instance of the UIView
type:
let movie = …
let viewModel = MoviesDetailsViewModel(movie: movie)
let detailsView = DetailsView(viewModel: viewModel)
view.addSubview(detailsView)
You have not shared where this Movie
parameter of the MoviesDetailsViewModel
comes from. Perhaps the movieDetails
property?