Programmatically made UICollectionViewController not showing cells - swift

The background is black and the cells are white though none of the cells are showing in the simulator? would any one know why this might be?
import UIKit
import Foundation
class ChatLog: UICollectionViewController, UITextFieldDelegate, UICollectionViewDelegateFlowLayout {
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = UIColor.black
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = UIColor.white
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.height, height: 80)
}
}

Update:
Initialising the ChatLog view controller programmatically as follows means that the UICollectionView datasource methods aren't called e.g collectionView(_:cellForItemAt:) isn't called.
let newViewController = ChatLog(collectionViewLayout: UICollectionViewLayout())
Replace with this:
let newViewController = ChatLog(collectionViewLayout: UICollectionViewFlowLayout())
--
As you have declared a UICollectionViewController, you don't actually need to set explicitly in code, the delegate and dataSource properties of your collection view.
Simply make sure that in the Main.storyboard, you have set the class of your UICollectionViewController to ChatLog by clicking on the view controller and then the identity inspector. Also ensure that you have clicked the UICollectionViewCell and set it's Identifier to "cellId".
If this is a multiple view controller project, ensure that it is possible to navigate to ChatLog view controller by making it initial view controller or providing segue/navigation to this view controller from another view controller.
Pictures below outline my solution.

Set the delegate and datasource of the collectionView. The datasource and delegate methods(numberOfItemsInSection, cellForItemAtIndexpath, etc) will be called only if you have set the delegate and datasource. You can set it either in code or in storyboard(if you have used storyboard to design the collectionView)
In the viewDidLoad you can add
collectionView.delegate = self
collectionView.datasource = self

Related

UICollectionView displays empty and doesn't show any cells

I am just testing the code to see if it can load the collection view. But id doesn't show anything. Please look at the code below and let me know where I am wrong.
import UIKit
private let reuseIdentifier = "Cell"
class FeedController: UICollectionViewController {
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
collectionView.dataSource = self
collectionView.delegate = self
self.collectionView.reloadData()
}
//MARK: - Helpers
func configureUI() {
collectionView.backgroundColor = .white
collectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
}
}
//MARK:- UICollectionViewDataSource
extension FeedController {
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath)
cell.backgroundColor = .systemRed
return cell
}
}
extension FeedController: UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
return CGSize(width: view.frame.width, height: 200)
}
}
Set a breakpoint somewhere in viewDidLoad to test if it even calls any code of this class when running the app.
Normally you get an error when you set the dataSource and delegate in a UICollectionViewController, because these are already set by the controller. Because of this, move the extension lines to the main class and remove the extension.
You need to initialize FeedController using public init(collectionViewLayout layout: UICollectionViewLayout) UICollectionViewController initializer, else you have no layout assigned to the UICollectionView.
Wherever you are doing FeedController() you should do FeedController(collectionViewLayout: UICollectionViewFlowLayout()) or some custom UICollectionViewLayout.

Segue from selected collection view cell sends to wrong collection view

I am using swift. I'm trying to create a collectionView segue to lead to a new viewController.
I have a series of (lets say 8) different images and labels as an array within the collectionView, and the when selected, I want the user to be sent to another view controller (with 8 different possibilities - one for each cell). I have been able to get the app to build, but the behaviour from selecting a cell is wrong.
The first cell that is selected has no response, then the next cell initiates a segue - but to the previously selected one! Each time a different cell is selected, it segues to the previous selected cell. Can anyone help me correct this error?
I have used performSegue and pushViewController separately, following different tutorials on youtube, but each resolves to the same issue.
The various view controllers to segue to have been allocated their own storyboardID in the main.storyboard file. The initial view controller is embedded within a navigation controller, and each new veiwcontroller (to segue to) has been connected to the collection view of the main view controller.
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet var CollectionView: UICollectionView!
// created an array for labels
let NamesForSectionTitles = ["Overview","Canopy trees","Mid-story trees","Understory plants","Birds","Mammals","Ferns","Butterflys"]
// created list of images to be loaded in the collectionview
let sectionIconImages: [UIImage] = [
UIImage(named: "IMG_8750_landscape_night")!,
UIImage(named: "IMG_8789_Trees")!,
UIImage(named: "IMG_2185_Tree_Olearia")!,
UIImage(named: "_MG_9528_Herb_Flower")!,
UIImage(named: "IMG_3654-")!,
UIImage(named: "IMG_9892-2")!,
UIImage(named: "IMG_9496_Ferns_crozier")!,
UIImage(named: "IMG_7707_Butterfly")!,
]
override func viewDidLoad() {
super.viewDidLoad()
// load the data sources and delegate
CollectionView.dataSource = self
CollectionView.delegate = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return NamesForSectionTitles.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) ->
UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! CollectionViewCell
cell.SectionTitle.text = NamesForSectionTitles[indexPath.row]
cell.sectionImages.image = sectionIconImages[indexPath.row]
return cell
}
// tells the collection view that upon selecting a cell, show the next UIView controller, as suggested in storyboard name (main.storyboard property) - tutoral from https://www.youtube.com/watch?v=pwZCksvXGRw&list=PLPUDRZDcNNsMdyfZVw4CJDT1Wu8cYx_6E&index=7&t=0s
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
performSegue(withIdentifier: NamesForSectionTitles[indexPath.row], sender: self)
}
}
The viewer should see an image view with a label, that when selected takes them to a new collection view.
Can't Comment so posting an answer
I think
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) is causing the issue.
Please try changing that line to:
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)

UICollectionViewCell not shown properly

I have the following code. For some strange reason, the cell is not registering properly and the cell is not rendered.
I expect there to be 5 purple cells but instead there is only red background.
class RateController: UICollectionViewController {
var user: User?
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.backgroundColor = .red
collectionView?.register(UICollectionViewCell.self, forCellWithReuseIdentifier: cellId)
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath)
cell.backgroundColor = .purple
return cell
}
}
There's nothing wrong with your code. I pasted it right into a fresh project, defined the User class, made the necessary adjustments in the storyboard, and ran it:
So if there's a problem, it's somewhere else — not in that code!
I've been thinking about where else the problem could be; the problem you are describing would happen if you disabled the Flow layout in the storyboard and replaced it with an empty Custom layout, like this:
If you did that, set the Layout back to Flow.

How to make collection view header and cells the same size

I have created a collection view with multiple sections. I want the header view and the collection view cells to be the same size.
I've tried several things:
Setting insets on the collection view, which seems to have no effect on the cells or the headers.
Changed referenceSizeForHeaderInSection which also seems to have no effect.
Changed the size of the view in the Xib associate with the reusable view.
Constrained the UILabel within the Xib to 0 from the top, left, right, and bottom and set the width the 300, but this seems to be overridden (see below, the blacked out areas in the picture are the app name).
Screenshot of constraints being broke
Below are some of my methods:
viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
collectionView.delegate = self
collectionView.dataSource = self
collectionView.contentInset = UIEdgeInsets(top: 30, left: 30, bottom: 0, right: 30)
collectionViewHeightAnchor.constant = view.frame.height / 2
let nib = UINib(nibName: "CollectionReusableView", bundle: nil)
collectionView.register(nib, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "groceryHeader")
}
Collection view delegate/datasource and header setup
// MARK: - Header
func numberOfSections(in collectionView: UICollectionView) -> Int {
return foodItems.count
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: 120, height: 45)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
let (key, _) = foodItems[indexPath.section]
let cell = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "groceryHeader", for: indexPath) as! CollectionReusableView
cell.lbl.text = key
return cell
}
// MARK: - Cell
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return foodItems[section].1.count
}
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
return CGSize(width: 120, height: 45)
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "foodCell", for: indexPath)
if let cell = cell as? FoodItemCell {
let (_, val) = foodItems[indexPath.section]
cell.titleLbl.text = val[indexPath.row].text
cell.quantityLbl.text = val[indexPath.row].quantity
cell.postId = val[indexPath.row].id
if let arr = UserDefaults.standard.getCheckedIds() {
cell.checkBoxImg.setImage(arr.contains(val[indexPath.row].id) ? UIImage(named: "checked") : UIImage(named: "unchecked"), for: .normal)
}
}
return cell
}
Thank you in advance.
Sounds like you need the width of your headers to be a certain size. The default for UICollectionViewFlowLayout is to fill the width of the collection view.
You have a few options here:
1) Create a custom layout that sizes the supplementary views (headers) at the desired size. This would probably be considered the "correct" option but is probably more complicated than you need.
2) Just add a "container" view to your header view, set the width to your desired size (120) and center X to the parent. If the root collection view element (UICollectionReusabableView) is transparent it won't show leaving your subview (the "container") as the only visible view.
3) Unless you are using UICollectionViewController you could just set the width of the collection view to your desired size (120). If you are using UICollectionViewController, you could replace it with a UIViewController, add a UICollectionView and set the dataSource and delegate to the UIViewController. This way the headers, under the default flow layout, would fill the cv, which would only be as wide as you need.

show viewController from collectionView without storyboards

I am having some trouble showing a viewController programmatically (without storyboards) from a collectionView. I would think this is pretty easy but I am having some difficulty figuring this out as I am somewhat new to swift. I believe collectionViewCells are not delegates by default, so I have tried implementing the didSelectItemAt in the collectionView class but still no luck. On tap of the cell I am receiving a print statement, just not having any show segue. See collectionViewCell code below, and thanks in advance!
// collectionViewCell class
class PlanningCell: BaseCell, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
lazy var collectionView: UICollectionView = {
let layout = UICollectionViewFlowLayout()
let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
cv.dataSource = self
cv.delegate = self
return cv
}()
//collectionViewCell variables
var plannedPlaces = [CurrentPlanner]()
let cellId = "cellId"
var basePlanningCell: BasePlanningCell!
override func setupViews() {
super.setupViews()
addSubview(collectionView)
collectionView.register(BasePlanningCell.self, forCellWithReuseIdentifier: cellId)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return plannedPlaces.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellId", for: indexPath) as! BasePlanningCell
cell.currentPlanner = plannedPlaces[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
print("did tap")
let plannersDetailVC = PlanningPlaceDetailsVC()
plannersDetailVC.navigationTitle = plannedPlaces[(indexPath.row)].planningPlace
// below I am receiving the use of unresolved identifier "show" error
show(plannersDetailVC, sender: self)
}
}
Normally, when you want to present another view controoler, you need to call this function
self.present(yourViewController, animated: true, completion: nil)
However, you cannot do that in the place you do the print because the function present is only available in view controllers. So now the problem becomes, how to direct your cell click to the parent view controller and pass a string. You have two options, you can either create a delegate like this, or do a NSNotificationCenter post call, the second one being easier.
When you pass the cell click event to your parent view controller, you can start calling that method mention above to navigate to another view controoler
let vc = UIStoryboard(name:"Main", bundle:nil).instantiateViewController(withIdentifier: "VCIdentifier") as! PlanningPlaceDetailsVC
present(vc, animated: true, completion: nil)
Don't forget to set the PlanningPlaceDetailsVC Identifier
Updated for a VC setted by code:
let vc : UIViewController = PlanningPlaceDetailsVC()
self.presentViewController(vc, animated: true, completion: nil)