Swift4 - 我第一次无法显示 CollectionView “无法同时满足约束”

Posted

技术标签:

【中文标题】Swift4 - 我第一次无法显示 CollectionView “无法同时满足约束”【英文标题】:Swift4 - I can not show the CollectionView the first time "Unable to simultaneously satisfy constraints" 【发布时间】:2018-11-14 13:22:13 【问题描述】:

我正在使用 Swift 4 创建一个应用程序,我在其中向 API 发出请求,我想在 CollectionView 上返回结果。

但我收到以下错误,我认为这是来自约束:

此块重复 100 次。

结果是他没有绘制任何单元格。显示这样的图像:

除非我按两次顶部按钮“更改自动布局”。当你绘制你拥有的两种显示模式的单元格时,它看起来像这样:

还有这个:

但问题是,最初什么都没有显示,应该显示。并且出现了我在开始时向您展示的错误。

为了对您有所帮助,因为我会说问题源于应用的约束,所以我附上了一些应用了不同约束的图像。

collectionView所在的初始xib是:

最初加载的单元格是:

我们改变布局后的单元格是这样的:

我附上了控制CollectionView的主类ViewVontroller的代码:

import UIKit
import RxSwift
import RxCocoa

final class SpeedRunListViewController: UIViewController 

@IBOutlet private var collectionView: UICollectionView!
@IBOutlet private var buttonChangeLayout: UIButton!

private let disposeBag = DisposeBag()
private var viewModelList: SpeedRunListViewModel?
private var numElementsByCol: CGFloat = 3

override func viewDidLoad() 
    super.viewDidLoad()
    navigationController?.isNavigationBarHidden = true
    setupCollectionView()
    viewModelList = SpeedRunListViewModel(interactor: InteractorSpeedRunSearch())
    setupRx(viewModel: viewModelList!)
    viewModelList?.fetch()


override func didReceiveMemoryWarning() 
    super.didReceiveMemoryWarning()


override func viewWillAppear(_ animated: Bool) 
    super.viewWillAppear(animated)
    navigationController?.isNavigationBarHidden = true



private func setupCollectionView() 
    registerCollectionCells()
    if #available(ios 10.0, *) 
        collectionView.isPrefetchingEnabled = false
     else 
        // Fallback on earlier versions
    
    calculateLayoutCollectionItem()



private func registerCollectionCells() 
    collectionView.register(UINib(nibName: SpeedRunRowCollectionViewCell.nibName, bundle: nil),
                            forCellWithReuseIdentifier: SpeedRunRowCollectionViewCell.reuseCellId)

    collectionView.register(UINib(nibName: SpeedRunCollectionViewCell.nibName, bundle: nil),
                            forCellWithReuseIdentifier: SpeedRunCollectionViewCell.reuseCellId)



private func calculateLayoutCollectionItem() 
    if let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout 
        layout.estimatedItemSize = CGSize.init(width: 2, height: 2)
    


private func setupRx(viewModel: SpeedRunListViewModel) 
    viewModel.numElements.asObservable().subscribe(onNext:  e in
        self.collectionView.reloadData()
    , onError:  error in

    , onCompleted: 

    , onDisposed: 

    ).disposed(by: disposeBag)


    buttonChangeLayout.rx.tap.subscribe(onNext:  void in
        guard let value = self.viewModelList?.layoutRow else 
            return
        
        self.viewModelList?.layoutRow = !value
        self.collectionView.collectionViewLayout.invalidateLayout()
        self.collectionView.reloadData()
    , onError:  error in

    , onCompleted: 

    , onDisposed: 

    ).disposed(by: disposeBag)



fileprivate func getCellId() -> String 
    if let layoutRow = self.viewModelList?.layoutRow, layoutRow == true 
        return SpeedRunRowCollectionViewCell.reuseCellId
    
    return SpeedRunCollectionViewCell.reuseCellId




//MARK: - UICollectionViewDelegate, UICollectionViewDataSource
extension SpeedRunListViewController: UICollectionViewDelegate, 
UICollectionViewDataSource 
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
    guard let numElements = viewModelList?.numElements else 
        return 0
    
    return numElements.value


func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: getCellId(), for: indexPath) as! SpeedRunCollectionViewCellBase
    if let cellViewModel = viewModelList?.getCellViewModel(index: indexPath.row) 
        cell.setupCell(viewModel: cellViewModel)
    

    return cell



func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) 
    guard let speedRun = viewModelList?.getSpeedRun(index: indexPath.row) else 
        return
    

    let interactorDetail: InteractorSpeedRunDetail = InteractorSpeedRunDetail(speedRun: speedRun)
    let viewControllerDetail: SpeedRunDetailViewController = SpeedRunDetailViewController(interactor: interactorDetail)
    viewControllerDetail.URISpeedRunDetail = (speedRun.links![1].uri)!

    navigationController?.pushViewController(viewControllerDetail, animated: true)




事实上,我不知道为什么会发生布局冲突。但这让我发疯......我无法理解最初没有显示单元格(因为正在接收数据)。会是什么?

非常感谢,有任何问题都附上我。

[代码更新]

这些是代码解决方案:

//MARK: - UICollectionViewDelegateFlowLayout
extension SpeedRunListViewController: UICollectionViewDelegateFlowLayout 

func collectionView(_ collectionView: UICollectionView,
                    layout collectionViewLayout: UICollectionViewLayout,
                    sizeForItemAt indexPath: IndexPath) -> CGSize


    if let value = self.viewModelList?.layoutRow 
        if value 
            return CGSize(width: 320, height: 144)
        
        else
            return CGSize(width: 96, height: 162)
        
    

    return CGSize(width: 320, height: 144)



【问题讨论】:

【参考方案1】:

您没有在 viewController 中设置 UICollectionViewDelegateFlowLayout。你需要设置它然后使用

func collectionView(_ collectionView: UICollectionView, 
                  layout collectionViewLayout: UICollectionViewLayout, 
           sizeForItemAt indexPath: IndexPath) -> CGSize

设置单元格的大小。

您遇到了错误,因为当您第一次加载单元格时,您基本上是在告诉他们它们的大小为 0 0。

【讨论】:

完美!谢谢@GaloTorresSevilla 这解决了我的问题:)!我已经更新了我的代码。

以上是关于Swift4 - 我第一次无法显示 CollectionView “无法同时满足约束”的主要内容,如果未能解决你的问题,请参考以下文章

无法将 .mm 文件访问到 swift4

如何删除空白存档的Xcode文件(swift4)

无法使应用程序进入主菜单(Swift 4 SpriteKit)

Swift 4.1 - 无法将 [Character] 类型的值转换为 [String]

如何实现 AutoFill Credential Provider Extensions - iOS 11, swift4

无法将坐标放入全局变量 [CLgeocoder, swift4]