I've been searching for days these answers and some how I found it but I don't know how to configure properly to my project.
I followed this awesome video iOS Dev 11: CollectionViews | Swift 5, XCode 11
This video works perfectly to me, but when we reach sizeForItemAt method he declares a static value. For my project, width could be static and I was able to fix according the result I want, but I am still facing difficult to adapt the height. I want it to be equal the text size of my label text, I want I result like this: final result
So, I know that I need to to this on sizeForItemAt method but I don't know how, here is how my code looks like:
FactsCollectionViewCell:
class FactsCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var lbFactsText: UILabel!
#IBOutlet weak var lbCategories: UILabel!
func setup(with facts: FactsData?, index: Int) {
let result = facts!.result[index]
lbFactsText.text = result.value
print(result.value)
if let category = result.categories.first {
lbCategories.text = category!.uppercased()
lbCategories.sizeToFit()
} else {
lbCategories.text = "UNCATEGORIZED"
lbCategories.sizeToFit()
}
}
#IBAction func btShare(_ sender: UIButton) {
// still working on this button
}
}
And this is my MainViewController:
class MainViewController: UIViewController {
var facts: FactsData?
var factsManager = FactsManager()
var numberOfCV = 0
var resultValue: String = ""
#IBOutlet weak var cvFacts: UICollectionView!
#IBOutlet weak var sbSearchFacts: UISearchBar!
#IBOutlet weak var nbMainScreen: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
cvFacts.dataSource = self
cvFacts.delegate = self
cvFacts.collectionViewLayout = UICollectionViewFlowLayout()
sbSearchFacts.isHidden = true
}
#IBAction func search(_ sender: UIBarButtonItem) {
sbSearchFacts.resignFirstResponder()
sbSearchFacts.isHidden = !sbSearchFacts.isHidden
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueMainToFact" {
let vc = segue.destination as! FactViewController
vc.receivedValue = resultValue
}
}
}
extension MainViewController: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return numberOfCV
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = cvFacts.dequeueReusableCell(withReuseIdentifier: "FactsCollectionViewCell", for: indexPath) as! FactsCollectionViewCell
cell.setup(with: facts, index: indexPath.row)
return cell
}
}
// SearchBar delegate extension for when tap to search button
extension MainViewController: UISearchBarDelegate {
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
sbSearchFacts.isHidden = true
sbSearchFacts.resignFirstResponder()
if let realFact = sbSearchFacts.text {
factsManager.factsRequest(realFact) { (facts) in
self.facts = facts
self.numberOfCV = self.facts!.total
DispatchQueue.main.async {
self.cvFacts.reloadData()
}
} onError: { (error) in
print(error)
}
}
}
}
// Extension to configure cell sizes
extension MainViewController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let width = view.frame.size.width
return CGSize(width: width - 40, height: //HERE I NEED TO CALCULATE THE HEIGHT BUT I DONT KNOW HOW)
}
}
// Extension for when you tap the cell
extension MainViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let index = indexPath.row
let result = facts?.result[index]
resultValue = result!.value
}
}
Does anyone has a tip to fix it?
Related
This question already has answers here:
passing tapped cell data to another view via segue in Swift
(2 answers)
Pass data through segue
(3 answers)
Closed 8 months ago.
Hi dear professionals.
I have main ViewController, where I put Three horizontal CollectionView with cells into (but I hope at least solve problem with 1 of these).
One of this named - FirstPlaylistCollectionView
Cells also custom - FirstPlaylistCollectionViewCell
On tap on cell with specific video it needed pass Video object to the Player (PlayerViewController).
I cant figure it out how, in my case, make this Segue (pass Video object with necessary data) from CollectionView by code !
I almost don't use Storyboard in this project.
Maybe with help of Delegate, but I'm also couldn't understand how to use them for my case.
Method didSelectItemAt - works and get Video object, but i don't understand how to pass it correctly.
Will be very grateful for answer. I couldn't apply for now any solution from Stack, help please.
FirstPlaylistCollectionView code
import UIKit
protocol FirstPlaylistCollectionViewDelegate: AnyObject {
func playVideo()
}
class FirstPlaylistCollectionView: UICollectionView, UICollectionViewDelegate, UICollectionViewDataSource, ModelDelegate {
var playlistsModel = PlaylistsModel()
private var firstPlaylist: [Video] = []
weak var delegate2: FirstPlaylistCollectionViewDelegate?
// MARK: - Data Source
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return firstPlaylist.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: FirstPlaylistCollectionViewCell.reuseId, for: indexPath) as! FirstPlaylistCollectionViewCell
let video = self.firstPlaylist[indexPath.row]
cell.setCell(video)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.delegate2?.playVideo()
print("selected video \(firstPlaylist[indexPath.row]) with \(collectionView)! DONE!")
}
FirstPlaylistCollectionViewCell code
class FirstPlaylistCollectionViewCell: UICollectionViewCell {
static let reuseId = "FirstPlaylistCollectionViewCell"
var video: Video?
PlayerViewController code
import UIKit
import WebKit
class PlayerViewController: UIViewController {
#IBOutlet weak var handleArea: UIView!
#IBOutlet weak var openCloseArrow: UIImageView!
var video: Video?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("I'm here!!!")
let vc2 = segue.destination as! PlayerViewController
if let cell = sender as? Video {
self.video = cell
vc2.titleOfVideoLabel.text = video?.title
}
}
}
extension PlayerViewController: FirstPlaylistCollectionViewDelegate {
func playVideo() {
performSegue(withIdentifier: "homeToPlayer", sender: self)
}
}
Answering this by assuming some of the things, I hope you want to navigate to PlayerViewController from ViewController through a segue. Keeping that in my mind, I have assumed your FirstPlaylistCollectionView is in your ViewController class as mentioned below.
class ViewController: UIViewController {
var firstPlaylistCollectionView: FirstPlaylistCollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// First try to get notified from your collection list to here
// and then from here to your player
firstPlaylistCollectionView.listDelegate = self
}
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
if let id = segue.identifier, id == "playerSegue",
let lVideo = sender as? Video,
let destination = segue.destination as? PlayerViewController{
destination.video = lVideo
}
}
}
extension ViewController: FirstPlaylistCollectionViewDelegate {
func firstPlaylistCollectionView(_ listView: FirstPlaylistCollectionView, didSlect video: Video) {
self.performSegue(withIdentifier: "playerSegue", sender: video)
}
}
And below is the update for the collection view
class FirstPlaylistCollectionView: UICollectionView {
var playlistsModel = PlaylistsModel()
private var firstPlaylist: [Video] = []
weak var listDelegate: FirstPlaylistCollectionViewDelegate?
}
extension FirstPlaylistCollectionView: UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return firstPlaylist.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = dequeueReusableCell(withReuseIdentifier: FirstPlaylistCollectionViewCell.reuseId, for: indexPath) as! FirstPlaylistCollectionViewCell
/* Here it goes your cell configuration
.
.
*/
return cell
}
}
extension FirstPlaylistCollectionView: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
listDelegate?.firstPlaylistCollectionView(self, didSlect: firstPlaylist[indexPath.row])
}
}
And finally verify that the playerViewController has received the data or not
class PlayerViewController: UIViewController {
#IBOutlet weak var handleArea: UIView!
#IBOutlet weak var openCloseArrow: UIImageView!
var video: Video?
override func viewDidLoad() {
super.viewDidLoad()
print("Video object from player vc :: \(video)")
}
}
Added protocol is
protocol FirstPlaylistCollectionViewDelegate: AnyObject {
func firstPlaylistCollectionView(_ listView: FirstPlaylistCollectionView, didSlect video: Video) ->Void
}
you can use Prepare for segue or Did Select Row method try these out.
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let selectedProgram = programy[indexPath.row]
let destinationVC = PlayerTableViewController()
destinationVC.programVar = selectedProgram
destinationVC.performSegueWithIdentifier("playerSegue", sender: self)
}
I am still learning swift, and I am trying to create a UICollectionView that would return the number of items that I set in the initial view controller using the slider, but my code doesn't work, how would I do this? Here is my code below:
class ViewController: UIViewController {
//MARK: - Outlets
#IBOutlet weak var firstLabel: UILabel! {
didSet {
firstLabel.text = "0"
}
}
#IBOutlet weak var firstSlider: UISlider! {
didSet {
firstSlider.value = 0
firstSlider.minimumValue = 0
firstSlider.maximumValue = 500
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func firstSliderAction(_ sender: UISlider) {
let firstSliderAction = Int(round(sender.value))
firstLabel.text = "\(firstSliderAction)"
}
}
// CollectionViewController
private let reuseIdentifier = "cell"
class CollectionViewController: UICollectionViewController {
var vc: ViewController!
override func viewDidLoad() {
super.viewDidLoad()
vc = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "ViewController") as? ViewController
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Int(vc.firstSlider.value)
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionCell
cell.backgroundColor = .green
return cell
}
}
First let me tell you why your code is not working as expected.
vc = UIStoryboard(name: "Main", bundle: nil)
.instantiateViewController(withIdentifier: "ViewController") as? ViewController
Every time when we initiate view controller from corresponding storyboard it will create new object of that view controller, so based on that vc object will not have those value which has been set by earlier user interaction, in-short for that object didSet haven't called yet.
Now how you can achieve the above,
Well first we need to observe how you are navigating from ViewController to CollectionViewController based on code it looks like you are using segue so you can do below stuff.
class ViewController: UIViewController {
#IBOutlet weak var firstLabel: UILabel!
#IBOutlet weak var firstSlider: UISlider!
var sliderValue:Int = 0 {
didSet {
firstLabel.text = "\(sliderValue)"
}
}
override func viewDidLoad() {
super.viewDidLoad()
setUpInitialValues()
}
func setUpInitialValues() {
firstSlider.value = 0
firstSlider.minimumValue = 0
firstSlider.maximumValue = 500
}
// Value changed action
#IBAction func firstSliderAction(_ sender: UISlider) {
self.sliderValue = Int(round(sender.value))
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "navigateCollection" {
if let destinationVC = segue.destination as? CollectionViewController {
destinationVC.sliderValue = self.sliderValue
}
}
}
}
private let reuseIdentifier = "Cell"
class CollectionViewController: UICollectionViewController {
var sliderValue:Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Register cell classes
self.collectionView!.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
self.collectionView.reloadData()
// Do any additional setup after loading the view.
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of items
return sliderValue
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
// Configure the cell
cell.backgroundColor = .yellow
return cell
}
}
I have a problem. I'm making an app that consumes an API from themoviedb and saves the movies in the Realm. On the main screen I am using the CollectionViewController, and when I touch the movie it will go to details. However when it goes to details nothing appears and when I select another film it brings the previous film.
Home:
import UIKit
import RealmSwift
class HomeViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UISearchBarDelegate {
#IBOutlet weak var searchbar: UISearchBar!
#IBOutlet weak var ListOfMovies: UICollectionView!
var movies : Results<Moviess>!
let realm = try! Realm()
var detailsMovies = Moviess()
override func viewDidLoad() {
super.viewDidLoad()
print(Realm.Configuration.defaultConfiguration.fileURL!)
ListOfMovies.delegate = self
ListOfMovies.dataSource = self
searchbar.delegate = self
movies = realm.objects(Moviess.self)
getRequest()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
func getObjects(filter: String) -> Results<Moviess>{
movies = realm.objects(Moviess.self).filter("title CONTAINS[cd] %#",filter)
return movies
}
func getRequest(){
print(movies.count)
if movies.count < 1 {
RequestData.requisicao { (result) in
switch(result){
case .success(let detalhe):
self.ListOfMovies.reloadData()
print(detalhe)
case .failure(let error):
print(error)
}
}
}}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return movies.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "listOfMovies", for: indexPath) as! HomeCollectionViewCell
let infos = movies[indexPath.item]
cell.configurationMovie(movie: infos)
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
detailsMovies = movies[indexPath.row]
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return UIDevice.current.userInterfaceIdiom == .phone ? CGSize(width: collectionView.bounds.width/2-20, height: 200) : CGSize(width: collectionView.bounds.width/3-20, height: 250)
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
print(searchText)
if !searchText.isEmpty{
let filtro = getObjects(filter: searchText)
print(filtro)
ListOfMovies.reloadData()
}else{
movies = realm.objects(Moviess.self)
ListOfMovies.reloadData()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if(segue.identifier == "details")
{
let viewController = segue.destination as! DetailViewController
print(detailsMovies)
viewController.conta = detailsMovies
}
}
}
Details:
import UIKit
import RealmSwift
class DetailViewController: UIViewController, UITableViewDataSource, UITableViewDelegate{
var conta: Moviess!
let realm = try! Realm()
#IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.dataSource = self
tableView.delegate = self
tableView.reloadData()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return [conta].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "details", for: indexPath) as! DetailsTableViewCell
let infos = [conta][indexPath.item]
cell.prepare(movie: infos!)
return cell
}
}
Moviess
import RealmSwift
import UIKit
class Moviess: Object{
#objc dynamic var id = 0
#objc dynamic var title = ""
#objc dynamic var overview = ""
#objc dynamic var poster = ""
#objc dynamic var isFavorites = false
override class func primaryKey() -> String? {
return "id"
}
convenience init (id: Int){
self.init()
self.id = id
}
override class func indexedProperties() -> [String] {
return ["isFavorites"]
}
func insertMovieData(list: Moviess){
do {
let realm = try! Realm()
try! realm.write({ () -> Void in
realm.add(list)
})
} catch let error as NSError{
print("insert error : \(error)")
}
}
func togleFavorite(){
try? realm?.write{
isFavorites = !isFavorites
}
}
}
I have no idea where I might be giving this "bug". If someone can help and explain what is going on it will be very useful.
Your problem is that prepare(for:sender:) is called before collectionView(_:, didSelectItemAt:). prepare(for:sender:) should not rely on collectionView(_:, didSelectItemAt:).
Your prepare(for:sender:) method should look like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UICollectionViewCell,
let indexPath = ListOfMovies.indexPath(for: cell) else { return }
if (segue.identifier == "details") {
let viewController = segue.destination as! DetailViewController
viewController.conta = movies[indexPath.item]
}
}
i have a collectionView class
class LC: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout{
//in LC
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let VC = segue.destination as? VC2 {
VC.Rpe = Pass
}
}
it's working fine , in VC2 i have a function when executed it should segue to the next cell in the collection view
i'm not sure how or what is the best way to do it (reload VC2 with the next collection view cell details?, or run the collection view functions programmatically )
update
import Foundation
import UIKit
class View2: UIViewController {
#IBOutlet var Q_Pic: UIImageView!
#IBOutlet var Q_que: UILabel!
var SelectedCell: Ques!
override func viewDidLoad() {
super.viewDidLoad()
Q_Pic.image = UIImage(named: SelectedCell.LIMG)
Q_que.text = SelectedCell.Q
}
#IBAction func herewego(_ sender: Any) {
print("when the user press this button it should take him directly to the next cell detail , i don't want the user to go back to collection view and choose the next cell")
}
}
data
let Q_A_TEST_MOH = [
Ques(Q: "Q1? ",LIMG: "1"),
Ques(Q: "Q2? ",LIMG: "2"),
Ques(Q: "Q3?",LIMG: "3"),
Ques(Q: "Q4?",LIMG: "4"),
Ques(Q: "Q5?",LIMG: "5")
]
struct Ques {
var Q : String
var LIMG: String
}
UICollectionViewController
import Foundation
import UIKit
class test:UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
#IBOutlet var CollectionView: UICollectionView!
var Levelssss: [Ques]!
var ToPass: Ques!
var SelectedCategory: String!
var Level: Int!
override func viewDidLoad() {
super.viewDidLoad()
CollectionView.delegate = self
CollectionView.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return Q_A_TEST_MOH.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "LevelCell2", for: indexPath) as? cell1 {
let r = Q_A_TEST_MOH[indexPath.item]
cell.congigureCell(EditLater: r)
return cell
}
return UICollectionViewCell()
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
ToPass = Q_A_TEST_MOH[indexPath.item]
performSegue(withIdentifier: "To", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let detalsVC = segue.destination as? View2 {
detalsVC.SelectedCell = ToPass
}
}
}
UICollectionViewCell
import UIKit
class cell1: UICollectionViewCell {
#IBOutlet var BB: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
BB.layer.cornerRadius = 10
}
func congigureCell(EditLater: Ques){
BB.setImage(UIImage(named: EditLater.LIMG), for: .normal)
}
}
Download project from here : download the project
Here is the fixed variant: https://www.dropbox.com/s/bc7ktktrbqg9x7t/test%202.zip?dl=0
Logic is simple: pass a whole array of data and index of selected object.
In VC2 on button click you just increment index and update content of your views.
Now you should just check is index not greater then count of element in array.
I am creating an app with Swift 4, where I make a request to the API and I want to return a result on a CollectionView.
But I get the following error, which I think is from constraints:
This block is repeated 100 times.
And the result is that he does not paint any cells. Showing an image like this:
Unless I press the top button "CHANGE AUTOLAYOUT" twice. Which is when you paint the cells of the two display modes you have, and it looks like this:
And this:
But the problem is, initially nothing is shown and should be shown. And the error that I show you in the beginning appears.
To help you a little, because I would say that the problem derives from the constrainst applied, I attach some images with the different constrainsts applied.
The initial xib, where the collectionView is, are:
The cell that is initially loaded is:
The cell once we have changed the layout is this:
I attached the code of the main class, the ViewVontroller that controls the CollectionView:
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)
}
}
And the truth is that I do not know why that conflict of layouts occurs. But it's driving me crazy ... I can not understand how the cells are not shown initially (because data is being received). What could it be?
Thank you very much, any question you attach it to me.
[CODE UPDATED]
These is the code solution:
//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)
}
}
You are not setting the UICollectionViewDelegateFlowLayout in the viewController. You need to set it and then use
func collectionView(_ collectionView: UICollectionView,
layout collectionViewLayout: UICollectionViewLayout,
sizeForItemAt indexPath: IndexPath) -> CGSize
To set the sizes of your cells.
You are having the error because when you load the cells the very first time you are basically telling them that they have a size of 0 0.