Swift4 - 我第一次无法显示 CollectionView "Unable to simultaneously satisfy constraints"
Swift4 - I can not show the CollectionView the first time "Unable to simultaneously satisfy constraints"
我正在用 Swift 4 创建一个应用程序,我向 API 发出请求,我想 return 在 CollectionView 上得到一个结果。
但我收到以下错误,我认为这是来自约束:
此块重复 100 次。
结果是他没有绘制任何单元格。显示这样的图像:
除非我按顶部按钮 "CHANGE AUTOLAYOUT" 两次。这是当你绘制你拥有的两种显示模式的单元格时,它看起来像这样:
还有这个:
但问题是,最初没有显示任何内容,应该显示。并且出现了我在开始时向您展示的错误。
为了给你一点帮助,因为我会说问题是由应用的约束引起的,我附上了一些应用了不同约束的图像。
collectionView 所在的初始 xib 是:
最初加载的单元格是:
我们改变布局后的单元格是这样的:
我附上了主要的代码class,控制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)
}
}
您没有在 viewController 中设置 UICollectionViewDelegateFlowLayout。您需要设置它然后使用
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
设置单元格的大小。
您遇到错误是因为当您第一次加载单元格时,您基本上是在告诉它们它们的大小为 0 0。
我正在用 Swift 4 创建一个应用程序,我向 API 发出请求,我想 return 在 CollectionView 上得到一个结果。
但我收到以下错误,我认为这是来自约束:
此块重复 100 次。
结果是他没有绘制任何单元格。显示这样的图像:
除非我按顶部按钮 "CHANGE AUTOLAYOUT" 两次。这是当你绘制你拥有的两种显示模式的单元格时,它看起来像这样:
还有这个:
但问题是,最初没有显示任何内容,应该显示。并且出现了我在开始时向您展示的错误。
为了给你一点帮助,因为我会说问题是由应用的约束引起的,我附上了一些应用了不同约束的图像。
collectionView 所在的初始 xib 是:
最初加载的单元格是:
我们改变布局后的单元格是这样的:
我附上了主要的代码class,控制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)
}
}
您没有在 viewController 中设置 UICollectionViewDelegateFlowLayout。您需要设置它然后使用
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
设置单元格的大小。
您遇到错误是因为当您第一次加载单元格时,您基本上是在告诉它们它们的大小为 0 0。