Collection View未展示预期相似电影数据问题求助
问题排查与修复方案
1. 视图层级错误(核心问题)
CommonViewController的combineView方法逻辑完全错误:
- 错误创建了新的
CommonViewController实例,未使用当前控制器作为容器 - 仅调用
addChild添加子控制器,但未将子控制器的view添加到父视图,也未完成子控制器生命周期的关联(如didMove(toParent:))
修复代码:
private func combineView() { let dvc = MovieDetailsViewController(viewModel: viewModel) let svc = SmiliarMovieViewController(viewModel: viewModel) // 添加子控制器到当前CommonViewController addChild(dvc) addChild(svc) // 将子视图添加到当前控制器的view view.addSubview(dvc.view) view.addSubview(svc.view) // 设置子视图约束(可根据需求调整布局) dvc.view.translatesAutoresizingMaskIntoConstraints = false svc.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ dvc.view.topAnchor.constraint(equalTo: view.topAnchor), dvc.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), dvc.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), dvc.view.bottomAnchor.constraint(equalTo: svc.view.topAnchor), svc.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), svc.view.trailingAnchor.constraint(equalTo: view.trailingAnchor), svc.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), svc.view.heightAnchor.constraint(equalToConstant: view.frame.width/2) ]) // 完成子控制器生命周期关联 dvc.didMove(toParent: self) svc.didMove(toParent: self) }
2. ViewModel数据更新顺序错误
fetchSimilarMovie中先更新state触发回调,再赋值moviePage,导致回调执行时moviePage仍为空数组,collectionView reload后无数据。
修复代码:
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): // 先赋值数据,再更新state触发回调 self.moviePage = page.results self.state = .pageLoaded(page) case .failure(let error): self.state = .error print(error) } } }
3. Cell标识符不匹配(潜在问题)
SimilierMovieCell的静态标识符定义为"CompanyCell",与类名不符易混淆,建议修正:
class SimilierMovieCell: UICollectionViewCell { static let identifier = "SimilierMovieCell" // ... 其他代码 }
4. Cell重复添加子视图(性能与显示问题)
每次调用configure都向contentView添加companyImageView,复用cell时会重复添加视图导致布局混乱,应将添加子视图的代码移至cell初始化方法:
class SimilierMovieCell: UICollectionViewCell { static let identifier = "SimilierMovieCell" private lazy var companyImageView: UIImageView = { let imageView = UIImageView() imageView.translatesAutoresizingMaskIntoConstraints = false imageView.contentMode = .scaleAspectFit return imageView }() override init(frame: CGRect) { super.init(frame: frame) setupViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupViews() { contentView.addSubview(companyImageView) let safeArea = contentView.safeAreaLayoutGuide NSLayoutConstraint.activate([ companyImageView.topAnchor.constraint(equalTo: safeArea.topAnchor), companyImageView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor), companyImageView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor), companyImageView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor) ]) } func configure(movieDetails: Movie) { if let path = movieDetails.posterPath { companyImageView.dm_setImage(posterPath: path) } else { companyImageView.image = nil } } }
5. 图片下载强制解包(崩溃风险)
dm_setImage中强制解包imageURL!,若URL无效会导致崩溃,改为可选绑定:
extension UIImageView { func dm_setImage(posterPath: String) { guard let imageURL = URL(string: "https://image.tmdb.org/t/p/w185/\(posterPath)") else { self.image = nil return } DispatchQueue.global().async { guard let data = try? Data(contentsOf: imageURL) else { DispatchQueue.main.async { self.image = nil } return } DispatchQueue.main.async { self.image = UIImage(data: data) } } } }
额外优化建议
- 避免使用
Data(contentsOf:)进行网络请求,推荐用URLSession(同步请求无超时处理,易阻塞线程) - 为collectionView添加占位图,处理无数据或图片加载失败的场景
- 实现图片缓存机制,避免重复网络请求
内容的提问来源于stack exchange,提问作者user23454452
相关产品推荐
相关产品推荐

