集合视图未显示预期数据

问题描述 投票:0回答:1

我想根据从表视图 didSelectRow 函数传递到集合视图的电影 ID 显示类似的电影。这是获取类似电影记录的 URL。 https://api.themoviedb.org/3/movie/129/similar?api_key=apikey。在这里,我传递电影 ID 129,它会获取类似的电影。我可以在调试控制台中看到我得到了正确的响应,但它没有在集合视图中显示类似的图像。它的代码有点多,因为我遵循编程方式来创建视图。

这是模型代码..这是通用结构,我在这里传递解码[电影]的电影。

struct Page<T: Decodable>: Decodable {
    
    let pageNumber: Int
    let totalResults: Int
    let totalPages: Int
    let results: [T]
    
    enum CodingKeys: String, CodingKey {
        case pageNumber = "page"
        case totalResults = "total_results"
        case totalPages = "total_pages"
        case results
    }
    
}

这是 URL 网络管理器类..

final class APIManager: APIManaging {
    
    static let shared = APIManager()
    
    let host = "https://api.themoviedb.org/3"
    let apiKey = "apikey"
    
    private let urlSession: URLSession
    
    init(urlSession: URLSession = .shared) {
        self.urlSession = urlSession
    }
    
    func execute<Value: Decodable>(_ request: Request<Value>, completion: @escaping (Result<Value, APIError>) -> Void) {
        urlSession.dataTask(with: urlRequest(for: request)) { responseData, response, error in
            if let data = responseData {
                let response: Value
                do {
                    response = try JSONDecoder().decode(Value.self, from: data)
                } catch {
                    completion(.failure(.parsingError))
                    print(error)
                    return
                }
                
                completion(.success(response))
            } else {
                completion(.failure(.networkError))
            }
        }.resume()
    }
    
    private func urlRequest<Value>(for request: Request<Value>) -> URLRequest {
        let url = URL(host, apiKey, request)
        var result = URLRequest(url: url)
        result.httpMethod = request.method.rawValue
        result.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
        return result
    }
    
}

这里的函数被称为表单视图模型..

static func similiar(for movieID: Int) -> Request<Page<Movie>> {
        return Request(method: .get, path: "movie/\(movieID)/similar")
    }

这里是表视图的didSelect函数。在这里,我选择单元格并导航到详细信息视图..

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    let movie = viewModel.state.movies[indexPath.row]
    let viewModel = MoviesDetailsViewModel(movie: movie, apiManager: APIManager())
    let viewController = MovieDetailsViewController(viewModel: viewModel)
    viewModel.fetchSimilarMovie(for: indexPath.row)
    self.navigationController?.pushViewController(viewController, animated: true)
}

这是通用视图控制器的代码..

class CommonViewController: UIViewController {
    
    private let viewModel: MoviesDetailsViewModel
    private var viewController : UIViewController!
    
    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() {
        viewController = CommonViewController(viewModel: viewModel)
        let dvc = MovieDetailsViewController(viewModel: viewModel)
        let svc = SmiliarMovieViewController(viewModel: viewModel)
        viewController.addChild(dvc)
        viewController.addChild(svc)
    }
    @objc private func didTapBack(_ sender: UIBarButtonItem) {
        navigationController?.popViewController(animated: true)
    }
}

这是视图模型类..

enum MoviesDetailsViewModelState {
    case loading(Movie)
    case loaded(MovieDetails)
    case pageLoaded(Page<Movie>)
    case error
    
    /// rest of the enum  here for movie and error..

    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
            case .failure(let error):
                self.state = .error
                print(error)
            }
        }
    }
}

这是我的集合视图代码。现在我正在尝试显示电影图像。

import UIKit

class SmiliarMovieViewController: 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")
    }
    private lazy var collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        let width = UIScreen.main.bounds.width
        layout.itemSize = CGSize(width: width, height: 80)

        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 = .lightGray
        return collection
    }()
    private lazy var movieTitleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textColor = .black
        label.numberOfLines = 0
        return label
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setUpUI()
        viewModel.fetchSimilarMovie()
        collectionView.reloadData()
    }
    
    private func setUpUI() {
        
        self.view.addSubview(collectionView)
        
        // constraints
        let safeArea = view.safeAreaLayoutGuide
        
        collectionView.topAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
        collectionView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
        collectionView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
        collectionView.heightAnchor.constraint(equalToConstant: 80).isActive = true
       
    }
}

extension SmiliarMovieViewController: UICollectionViewDataSource {
    
    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()
    }
}

这是手机号码。

class SimilierMovieCell: UICollectionViewCell {
    
    static let identifier = "CompanyCell"
    
    private lazy var companyImageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFit
        return imageView
    }()
    
    func configure(movieDetails: Movie) {
        contentView.addSubview(companyImageView)
        if let path = movieDetails.posterPath {
            companyImageView.dm_setImage(posterPath: path)
        } else {
            companyImageView.image = nil
        }
        let safeArea = contentView.safeAreaLayoutGuide
        companyImageView.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true
        companyImageView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor).isActive = true
        companyImageView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor).isActive = true
        companyImageView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor).isActive = true
    }
}

这里是下载图片的功能..

func dm_setImage(posterPath: String) {
        let imageURL = URL(string: "https://image.tmdb.org/t/p/w185/\(posterPath)")
        DispatchQueue.global().async {
            let data = try? Data(contentsOf: imageURL!)
            DispatchQueue.main.async {
                guard let data = data else { return }
                self.image = UIImage(data: data)
            }
        }
    }

这是结果的截图。当我选择 tableview didselect 并在此处传递电影 ID 表单时,我希望在视图底部显示类似的电影详细信息,现在它是空的。

ios swift uitableview uicollectionview
1个回答
0
投票

fetchsimilarMovie()
内部
MoviesDetailsViewModel
调用apiManager内部定义的异步方法。异步执行不会阻塞代码,也不保证得到结果的时间。因此,在获得结果后必须添加逻辑来更新 UI。

在您的例子中,您在收到

MoviesDetailsViewModel
函数的结果后编写了代码来更改
apiManager.execute
的状态。但是,您没有设置
updatedState
闭包应该做什么。您需要将其设置为
collectionView.reloadData()
应该执行的操作,而不是仅在
viewDidLoad()
SmiliarMovieViewController
内调用
updatedState

class SmiliarMovieViewController: UIViewController {

    private let viewModel: MoviesDetailsViewModel

    init(viewModel: MoviesDetailsViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
        navigationItem.largeTitleDisplayMode = .never
    }
    //...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setUpUI()
        
       // Set `updatedState`
        self.viewModel.updatedState = { [weak self] in
            self.collectionView.reloadData()
        }
        
        viewModel.fetchSimilarMovie()
        
    }

希望这能达到你想要的效果。

© www.soinside.com 2019 - 2024. All rights reserved.