You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICollectionView渲染异常:跳转后无法正常显示布局求助

UICollectionView 布局渲染异常问题排查与修复

问题描述

点击UITableView单元格后跳转至SmiliarViewController,该控制器内的UICollectionView无法正常渲染布局,预期的横向滚动列表无法显示。

跳转逻辑代码

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

SmiliarViewController 代码

import UIKit

class SmiliarViewController: 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")
    }
    
    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 = .brown
        return cv
    }()
   
    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(collectionView)
        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 SmiliarViewController: UICollectionViewDataSource {
    
    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()
    }
}

问题截图

UICollectionView布局异常截图

问题分析与修复方案

1. 约束错误导致CollectionView移出屏幕

setUpUI中设置CollectionView顶部约束时,错误地将其与view.bottomAnchor对齐,导致整个CollectionView被移出屏幕视野。

修复:
将顶部约束改为与安全区域顶部对齐:

collectionView.topAnchor.constraint(equalTo: safeArea.topAnchor).isActive = true

2. 未设置CollectionView的DataSource和Delegate

代码中实现了UICollectionViewDataSource协议,但未将collectionView.dataSource和collectionView.delegate设置为当前控制器。另外,sizeForItemAt属于UICollectionViewDelegateFlowLayout协议,需要额外遵守该协议。

修复:
在viewDidLoad的setUpUI之后添加:

collectionView.dataSource = self
collectionView.delegate = self

同时更新协议声明:

extension SmiliarViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    // ... 原有代码
}

3. Cell复用标识符不匹配

注册Cell时使用的标识符是"CompanyCell",但dequeue时使用的是SimilierMovieCell.identifier,如果两者不一致会导致无法正确复用Cell,返回默认空Cell。

修复:
统一复用标识符,建议在SimilierMovieCell中定义静态属性:

static let identifier = "SimilierMovieCell"

然后修改注册代码:

cv.register(SimilierMovieCell.self, forCellWithReuseIdentifier: SimilierMovieCell.identifier)

4. 重复调用接口请求

跳转逻辑中已经调用了viewModel.fetchSimilarMovie(),在viewDidLoad中又重复调用,可能导致重复请求和数据混乱。

修复:
删除viewDidLoad中的viewModel.fetchSimilarMovie()调用,保留跳转时的一次调用即可。

修复后的完整SmiliarViewController代码

import UIKit

class SmiliarViewController: 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")
    }
    
    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: SimilierMovieCell.identifier)
        cv.backgroundColor = .brown
        return cv
    }()
   
    override func viewDidLoad() {
        super.viewDidLoad()
        setUpUI()
        collectionView.dataSource = self
        collectionView.delegate = self
        self.viewModel.updatedState = {[weak self] in
            DispatchQueue.main.async {
                self?.collectionView.reloadData()
            }
        }
    }
    
    private func setUpUI() {
        view.addSubview(collectionView)
        let safeArea = view.safeAreaLayoutGuide
        collectionView.topAnchor.constraint(equalTo: safeArea.topAnchor).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 SmiliarViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
    
    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 {
        return viewModel.moviePage.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: SimilierMovieCell.identifier, for: indexPath) as? SimilierMovieCell else {
            return SimilierMovieCell()
        }
        let listMovie = viewModel.moviePage[indexPath.row]
        cell.configure(listMovie)
        return cell
    }
}

内容的提问来源于stack exchange,提问作者user23454452

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 17:42:24