Search code examples
iosswiftuiviewuiviewcontrolleraddsubview

Adding two UIView vertically


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.

enter image description here


Solution

  • 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?