Trouble added cell to UICollectionView - swift

Trying to add a cell to my ViewController, and I'm having trouble. I'm wondering if I have all my delegates. Currently, I can change the color of the collection view's background, but can't add any cells.
I'm using an array of text from my Listing() object to drive the collection view cells. On viewDidLoad the length is 4. I envisioned using this count for the numberofItemsInSection, however, if I put the print statement there it is never printed. Which makes me think the wrong delegate is called and the method is never hit. Correct?
import UIKit
class ListingDetailViewController: UIViewController,
UICollectionViewDelegateFlowLayout {
var listing = Listing()
// #IBOutlet var image: UIImageView!
#IBOutlet var post: UITextView!
#IBOutlet var price: UILabel!
#IBOutlet var amenitiesCollection: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
navigationItem.title = listing.title
self.amenitiesCollection!.backgroundColor = UIColor.gray
print(listing.amenities.count)//printed 4
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int{
print(listing.amenities.count)
return listing.amenities.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier = "UICollectionViewCell"
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath)
return cell
}
}

Make sure you have linked the CollectionView delegate and datasource to your view controller.
In viewDidLoad() add:
self.amenitiesCollection.delegate = self
self.amenitiesCollection.dataSource = self
==================================
EDIT:
If you have already linked the delegate and dataSource, try reloading the collection view in viewDidAppear(_:)
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.amenitiesCollection.reloadData()
}

Related

Cells for CollectionView Are Not All Showing

The code below is my test files for my final project that will look like this. As you can see I will be scrolling by rows of one or two.
I have figured out how to get the horizontal scroll to work, however, it only displays two out of the six buttons as shown here in the simulator. As You can tell, it just repeats itself. I am needing the app to show app six buttons respectively.
The code for this:
var multiButton = [String]()
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var multiButton = ["faceButton", "faceButtonTwo", "faceButtonThree", "faceButtonFour", "faceButtonFive", "faceButtonSix"]
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return multiButton.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell=collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! MyCollectionViewCell
// cell.multiButton.layer.cornerRadius=50.0
return cell
// cell.myWebSeriesImage.image=UIImage(named: webSeriesImages[indexPath.row])
// return cell
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
I have a separate file with all my IBOutlets as shown in this code:
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
// var multiButton = ["faceButton", "faceButtonTwo", "faceButtonThree", "faceButtonFour", "faceButtonFive", "faceButtonSix"]
#IBOutlet weak var faceButtonFour: UIButton!
#IBOutlet weak var faceButtonFive: UIButton!
#IBOutlet weak var faceButtonThree: UIButton!
#IBOutlet weak var faceButtonTwo: UIButton!
#IBOutlet weak var faceButton: UIButton!
#IBOutlet weak var faceButtonSix: UIButton!
I have double checked the Identifiers, classes (class for cells are set for "myCollectionViewCell", which is the same file all IBOutlets are in), made sure all UIButtons were Installed, and have tried to find any abnormalities in the Attributes and Identity Inspector.
I also would like to address the issue of not being able to scroll horizontal without clicking outside the button.
UPDATE for Post:
This is what we are seeing currently after a previous comment to resize the collectionview. It continues to repeat over and over and I am struggling to find the reasons in my code.
This is a screenshot of main.storyboard.
This is very basic. You need to create something like this:
First create a collectionView with required height (50 for example) and add a button inside its cell. Give any width or height to the button (maybe 80 x 30) and add leading, trailing, top and bottom constraints. Make sure to change scroll direction to horizontal as required by you.
After that create a collectionView class and connect your button outlet to it:
import UIKit
class ButtonCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var button: UIButton!
override func awakeFromNib() {
super.awakeFromNib()
}
}
In your main ViewController, connect collectionView and add delegate & datasource function:
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var collectionViewButtons: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
collectionViewButtons.delegate = self
collectionViewButtons.dataSource = self
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 6 //number of buttons
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! ButtonCollectionViewCell
cell.button.setTitle("Button\(indexPath.item)", for: .normal) //set button title
if indexPath.item == 0 { //first button
cell.button.backgroundColor = UIColor.purple //set button background
}
else if indexPath.item == 1 { //second button
cell.button.backgroundColor = UIColor.red
}
else if indexPath.item == 2 { //3rd button
cell.button.backgroundColor = UIColor.systemGreen
}
else { // for remaining buttons
cell.button.backgroundColor = UIColor.darkGray
}
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if indexPath.item == 0 { // opens any page by clicking button 1
let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC1") as! ViewController1
navigationController?.pushViewController(vc, animated: true)
}
else if indexPath.item == 1 {
let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC2") as! ViewController2
navigationController?.pushViewController(vc, animated: true)
}
else if indexPath.item == 2 {
let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC3") as! ViewController3
navigationController?.pushViewController(vc, animated: true)
}
else {
let vc = storyboard?.instantiateViewController(withIdentifier: "anyVC4") as! ViewController4
navigationController?.pushViewController(vc, animated: true)
}
}
}
You can return any number of buttons by changing return 6 to any required number or use any array which can further be used to set properties of buttons created.
The didSelectAnItem can be used to add functionality when a cell is tapped.
In the end you should get output like this where you can scroll horizontally to see remaining buttons:

Swift UITableView / UICollectionVIew passing data to cell in cellForRowAt returns nil

I am trying to pass a struct from collection view to its cells in cellForRowAt method. I also tried using table view but it produces the same bug.
Collection view class code:
class PaintingListCollectionViewController: UICollectionViewController {
var allPaintings: CategorySection!
override func viewDidLoad() {
super.viewDidLoad()
...
}
// MARK: UICollectionViewDataSource
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return allPaintings.paintings.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "paintingCell", for: indexPath) as? PaintingCollectionViewCell
// works perfectly
cell?.paintingTitleLabel.text = allPaintings.paintings[indexPath.item].title
cell?.paintingImageView.image = UIImage(named: allPaintings.paintings[indexPath.item].title)
cell?.paintingData = allPaintings!.paintings[indexPath.item]
// prints out two valid data
print(cell?.paintingData, allPaintings!.paintings[indexPath.item])
return cell!
}
}
Cell class code:
class PaintingTableViewCell: UITableViewCell {
#IBOutlet weak var paintingImageView: UIImageView!
#IBOutlet weak var paintingNameLabel: UILabel!
var paintingData: Painting?
override func awakeFromNib() {
super.awakeFromNib()
// always returns nil, despite printing out the exact same thing in cellForItemAt returns a valid result.
print(paintingData)
}
}
I am using following structs:
struct CategorySection {
var title: String
var image: String
var color: UIColor
var paintings: [Painting]
}
struct Painting {
var title: String
var size: String
var year: String
var text: String
}
What I found is that if I pass data directly to the cell's #IBOutlet UI properties, it works. But when I am passing data to an optional variable in UITableViewCell class, the result is always nil. This problem happens if I use UITableView too. Also, I need to mention that var allPaintings: CategorySection! in TableView class is also passed from another table view controller using the exact same way as described here. So the data passing occurring here is already the second time of the chain.
It is a very weird bug... I will be very thankful if anyone can help me!
EDITED: The original problem description uses UITable view instead of collection view, and sections instead of row for datasource. I made these changes for debugging purposes.
If you put a breakpoint on the print statements in awakeFromNib and in cellForRowAt, you will see that awakeFromNib gets called before you put the data into the collectionViewCell. In fact it gets called by collectionView.dequeueReusableCell(). You want to use UICollectionViewCell.didMoveToSuperview()
class PaintingCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var paintingImageView: UIImageView!
#IBOutlet weak var paintingNameLabel: UILabel!
var paintingData: Painting?
override func awakeFromNib() {
super.awakeFromNib()
// always returns nil, despite printing out the exact same thing in cellForItemAt returns a valid result.
print("Awake From Nib", paintingData)
}
override func didMoveToSuperview() {
print ("did Move To Superview", paintingData)
}
}
I added a guard to error check the cellForRowAt, if for some reason it fails to create the cell, the final line of the function will crash
return cell!
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "paintingCell", for: indexPath) as? PaintingCollectionViewCell else {
fatalError("unable to dequeue cell")
}
// works perfectly
cell.paintingNameLabel?.text = allPaintings.paintings[indexPath.item].title
cell.paintingImageView.image = UIImage(named: allPaintings.paintings[indexPath.item].title)
cell.paintingData = allPaintings!.paintings[indexPath.item]
// prints out two valid data
print("cellForItemAt ",cell.paintingData ?? "nothing to see here", allPaintings!.paintings[indexPath.item])
return cell
}

UICollectionView Cells overlapping with each other when i use a button to increase cell height

I have a CollectionView setup and i have a button inside that increases the cell height by a fixed amount. However when it does this is overlapps with the cell below meaning the cell below doesnt move down to make space.
Ive been trying to figure this out for days and i cant seem to get it working.
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
var colors = [UIColor.red,UIColor.blue]
#IBOutlet weak var col: UICollectionView!
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 2
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! cellCollectionViewCell
cell.frame.size.height = CGFloat(317 + cell.button.tag*45)
cell.backgroundColor = colors[indexPath.row]
return cell
}
#IBAction func increaseHeight(_ sender: Any) {
col.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
class cellCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var butt: UIButton!
#IBAction func incHeight(_ sender: UIButton) {
sender.tag = sender.tag + 1
}
}
Editing the frame of the cell directly is probably not the best approach. Instead override collectionView sizeForItemAtIndexPath, and specify the height change there. This should allow the collection view to re-layout correctly with the new cell height.
You can see other questions like this one for more details

IBOutlet pageControl is invalid

I trying to add pageControl in viewController for display current page and total pages.
I have collectionView inside viewController. But if i add #IBOutlet in viewController i get the error:
"The pageControl outlet from the DetailViewController to the UIPageControl is invalid. Outlet cannot be connected to repeating content."
That means repeating content?
I understand that this question has been asked many times, but I tried what was suggested but nothing helped.
PageControl in main.storyboard have only one #IBOutlet and it throws an error...
If need my code, he is here:
class DetailViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UITableViewDelegate, UITableViewDataSource {
// MARK: - IBOutlet's
#IBOutlet weak var collectionView: UICollectionView!
#IBOutlet weak var tableView: UITableView!
#IBOutlet weak var pageControl: UIPageControl!
var hallImages: Hall?
var currentPage = 0
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
tableView.estimatedRowHeight = 626
tableView.rowHeight = UITableViewAutomaticDimension
}
// MARK: - CollectionView
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
guard let imagesHall = hallImages?.ImagesHalls.count else {
return 0
}
return imagesHall
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! DetailCollectionViewCell
if let imagesHalls = hallImages?.ImagesHalls[indexPath.item] {
cell.imageView.sd_setImage(with: URL(string: imagesHalls))
}
return cell
}
}
How i can add pageControll in collectionView or collectionCell (i don't know how is better)?
I had the same problem. It is because your page control is a subview of the cell. You cannot do this.
take it out of your cell
put it in your main view/ safe area

Putting Tableview inside UITableViewCell Swift

I'm having trouble with putting a tableview inside a uitableview cell. Right now, it doesn't even display the nested tableview. I set the tableview's delegates and datasource to the custom cell, and have all the required methods. I am also using a storyboard for my tableviews, and have connected all outlets properly.
The problem is that it does not go into the cellForRow function. It does, however, go into the numberOfRowsInSection function, and also the heightForRow function. Below is the code for the custom cell that holds the nested UITableviewcell.
import UIKit
class EventSurveyCell: UITableViewCell, UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var cellBox: UIView!
#IBOutlet weak var announcementLabel: UILabel!
#IBOutlet weak var descriptionLabel: UILabel!
#IBOutlet weak var dateLabel: UILabel!
#IBOutlet weak var authorLabel: UILabel!
#IBOutlet weak var numVotesLabel: UILabel!
#IBOutlet weak var tableView: UITableView!
var surveyArray: [SurveyClass] = []
override func awakeFromNib() {
super.awakeFromNib()
tableView.delegate = self
tableView.dataSource = self
// Initialization code
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
tableView.delegate = self
tableView.dataSource = self
// Configure the view for the selected state
}
func do_table_refresh()
{
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
print("cellforrow")
let cell: SurveyItemCell = self.tableView.dequeueReusableCellWithIdentifier("surveyItemCell") as! SurveyItemCell!
cell.itemLabel.text = surveyArray[indexPath.row].itemName
//go through firebase to check which one user voted on
cell.voteButton.imageView!.image = UIImage(named: "circle")
let percent = surveyArray[indexPath.row].numOfVotes/surveyArray[indexPath.row].totalNumOfVotes
cell.precentLabel.text = String(percent)
return cell
}
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
print("heightforrow")
return 44
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print("numrowsinsection")
return 3
//should be return self.surveyArray.count.
}
}
Below is a snippet of code for in the the viewcontroller that is holding the custom cells (containing nested tableviews). This code is part of the cellforrowatindexpath
case PostType.survey:
let cell: EventSurveyCell = self.tableView.dequeueReusableCellWithIdentifier("EventSurveyCell") as! EventSurveyCell!
//cut out other code as it is irrelevant
cell.surveyArray = eventPost.surveyClassArray
cell.do_table_refresh()
print(cell.tableView.rowHeight)
return cell
Another problem I have noticed is that when printing cell.tableview.rowheight, it prints -1. This could be the reason why it does not enter cellforrowatindexpath, but I clearly return 44 in the heightforrow function. Right now, it doesnt even display the nested tableview.
1) Don't need do_table_refresh, you can call cell.tableView.reloadData() directly.
2) Override the systemLayoutSizeFitting:withHorizontalFittingPriority:verticalFittingPriority function:
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
tableView.reloadData()
// if the table view is the last UI element, you might need to adjust the height
let size = CGSize(width: targetSize.width,
height: tableView.frame.origin.y + tableView.contentSize.height)
return size
}
3) In your view controller, after you set the surveyArray, remove this line cell.do_table_refresh(), replace with cell.setNeedsLayout() to auto adjust the layout (you might also need to call a method to update the other UIs such as announcementLabel etc. call it before setNeedsLayout too.)
I had the same issue, cellForRow was not being called for the TableView Inside Custom Cell. And the reason was that the tableview had no height set for it to show the content inside it. After I set some height for the child TableView, cellForRow started to begin calling.