I have a problem with a simple collection view: in the delegate didSelectItemAtIndexPath I can't get the right reference to the cell. This is my code:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int
{
return 1
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return categories.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CategorySelectionCVCell
cell.titleLabel.text = categories[indexPath.row]["name"] as? String
cell.backgroundColor = ConversionUtilities.getUIColorFrom(categories[indexPath.row]["color"] as! String)
return cell
}
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CategorySelectionCVCell
print("Cell with title: \(cell.titleLabel.text) and background color: \(cell.backgroundColor)")
collectionView.reloadData()
}
I get always when I select a cell:
Cell with title: Optional("Label") and background color: nil
and I don't know why, it's like the cellForItemAtIndexPath is not working but the same print in cellForItemAtIndexPath give me:
Cell with title: Optional("fruits") and background color:
Optional(UIDeviceRGBColorSpace 1 1 0 1)
...and yes, the cell identifier is right is "cell" and I also set up correctly in storyboard. In addition UICollectionViewDataSource and UICollectionViewDelegate are also correctly linked in IB.
Any help?
When you call dequeueReusableCellWithReuseIdentifier method, i.e.
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("cell", forIndexPath: indexPath) as! CategorySelectionCVCell
you get an empty cell ready to be reused, not the cell that is currently in your collection view.
Although you can get the cell from the collection view by calling
let cell = collectionView!.cellForItemAtIndexPath(indexPath) as! CategorySelectionCVCell
if all you need is the text from its label it's better to go straight to the model, i.e.
let labelText = categories[indexPath.row]["name"] as? String
Related
I am currently working on a project where I have one CollectionViewController and multiple cells registered. I need assistance with returning the number of cells I want to be shown for the each cell.
For Example I have registered:
collectionView?.register(PhotoCell.self, forCellWithReuseIdentifier: picCell)
collectionView?.register(ArticleCell.self, forCellWithReuseIdentifier: articleCell)
As you can see I have registered two cells. How can I return 7 photo cells and 4 article cells to be displayed in my collection view programmatically?
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 7
}
As of right now I am only able to return 7 photo cells successfully.
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return data.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PhotoCell //Default will never Execute
if data == photo (
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! PhotoCell
return cell )
else if data == article {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "Cell", for: indexPath) as! articleCell
return cell
}
return cell
}
use this approach to determine the cell type . Here Your data if its audio than that cell weill be used and if its article than other cell will be used and count return will be total data count.
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
{
return 11
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell
{
if (indexPath.row < 7)
{
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: picCell, for: indexPath) as? PhotoCell
{
//your code here
return cell
}
}
else if (indexPath.row > 6)
{
//if indexing is needed create new index starting at 0
let articeCellindex = indexPath.row - 7
if let cell = collectionView.dequeueReusableCell(withReuseIdentifier: articleCell, for: indexPath) as? ArticleCell
{
return cell
}
}
else
{
return UICollectionViewCell()
}
}
Amey's answer is fine but they omitted how to structure your cell data in a way so that both cell types are contained in one structure. An alternative is to explicitly state which rows will display the cell data. Make sure both the picCell and articleCell reuse identifiers refer to the same cell.
I am trying on my viewDidLoad() to fetch images from my DB and then present the images, and highlight the first one.
My code thus far is:
profile?.fetchImages(onComplete: { (errors, images) in
for error in errors {
print("Error", error)
}
for imageMap in images {
self.photos.append(imageMap.value)
}
if self.photos.count < self.MAX_PHOTOS {
self.photos.append(self.EMPTY_IMAGE)
}
DispatchQueue.main.async {
self.photoCollectionView.reloadData()
}
self.updateSlideshowImages()
let indexPath = IndexPath(row: 0, section: 0)
let cell = self.photoCollectionView.cellForItem(at: indexPath)
if cell != nil {
self.setBorder(cell!)
}
})
However, for me cell is always nil, despite images existing and being fetched, and thus the setBorder is never called. Why is the cell always nil? I just want to set the border on the first cell.
You have placed self.photoCollectionView.reloadData() in async block so the let cell = self.photoCollectionView.cellForItem(at: indexPath) will run immediately before collection view reload, thats the reason you are getting nil for first cell.
You need to make sure that after collection view reload, I mean when all collection view cells are loaded then you can get and do your operations.
Alternatively, you can do this in cellForItemAtIndexPath like below...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellIdentifier.jobCollectionViewCellIdentifier, for: indexPath) as! <#Your UICollectionViewCell#>
if indexPath.section == 0 && indexPath.row == 0 {
//highlight cell here
}
return cell
}
And if you want to highlight cell after all images load in collection view then you need to check for datasource of collection view.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = ...
if indexPath.row == arrImages.count-1 {
let newIndexPath = IndexPath(row: 0, section: 0)
if let cellImg = self.photoCollectionView.cellForItem(at: newIndexPath) {
self.setBorder(cellImg)
}
}
}
Instead of highlighting the UICollectionViewCell in viewDidLoad() after receiving the response, highlight it in willDisplay delegate method, i.e.
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath)
{
if indexPath.row == 0
{
cell.isHighlighted = true
}
}
And add border of cell in the isHighlighted property of UICollectionViewCell according to the cell highlight status, i.e.
class CustomCell: UICollectionViewCell
{
override var isHighlighted: Bool{
didSet{
self.layer.borderWidth = self.isHighlighted ? 2.0 : 0.0
}
}
}
You can use isSelected property the same way in case you want to change appearance of the cell based on the selection status.
Let me know if you still face any issue.
Set your cell's border in cellForRow method.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cellIdentifier", for: indexPath) as! YourCustomCell
if indexPath.row == 0 {
self.setBorder(cell)
}
return cell
}
I have a custom Collection view in a Viewcontroller , whenever my Collection View is loaded always I get my first cell empty. How to remove this empty cell.
//CollectionView functions
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return collectionData.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionCell
let collectionData = self.collectionData[indexPath.row]
cell.NameLbl.text = collectionData["name"] as? String
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let Info: NSDictionary = self.collectionData[indexPath.item] as! NSDictionary
let vc = self.storyboard?.instantiateViewController(withIdentifier: "edit") as! editVC
self.navigationController?.pushViewController(vc, animated: false)
}
Depends on what is the case that you should handle hiding the first element, there are tow options that you might want to implement:
1- If it is okay to remove the first object from the data source (collectionData array), then you can simply remove the first element from it:
In your view controller (viewDidLoad()) you could implement:
override func viewDidLoad() {
.
.
.
collectionData.remove(at: 0)
.
.
.
}
2- if you are required to keep the collectionData as is without removing the first element, but it should not be displayed in the UI, you will need to implement the UICollectionViewDataSource as follows:
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// subtract 1
return collectionData.count - 1
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "cell", for: indexPath) as! collectionCell
// add 1 to indexPath.row
let collectionData = self.collectionData[indexPath.row + 1]
cell.NameLbl.text = collectionData["name"] as? String
return cell
}
That should lead to displaying the collection view as expected without editing the collectionData data source array.
This may be a little confusing, but i have a view controller that has a table view that contains a custom table cell and within this table cell, I have a collectionView with each cell holding a single image. I am trying to configure my collectionCell's image using the userToView variable seen below.
The delegate function below is where the table cell is being configured:
OtherUserAccountViewController.swift
class OtherUserAccountViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var userToView = User()
...
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:OtherUserPhotosTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: photosCellReuseIdentifier) as! OtherUserPhotosTableViewCell
cell.selectionStyle = .none
return cell
}
...
}
The delegate function below is where this image is being set:
OtherUserPhotosTableViewCell.swift
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "otherUserPhotosCollectionCell", for: indexPath) as! OtherUserPhotosCollectionViewCell
//Configure cell with photo from parent ViewController
return cell
}
My custom CollectionViewCell can be seen below:
OtherUserPhotosCollectionViewCell.swift
class OtherUserPhotosCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var imageView: UIImageView!
}
I have only really been getting accustomed to Swift & iOS development over the last couple weeks so any help is much appreciated. Thank you for your time!
Answered by assuming that you have UIImages already available to you
Assuming you have a single UICollectionViewCell
In OtherUserPhotosTableViewCell.swift
var photoFromTableView: UIImage?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "otherUserPhotosCollectionCell", for: indexPath) as! OtherUserPhotosCollectionViewCell
//Configure cell with photo from parent ViewController
if let photo:UIImage = photoFromTableView {
cell.imageView.image = photo
}
return cell
}
In OtherUserAccountViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:OtherUserPhotosTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: photosCellReuseIdentifier) as! OtherUserPhotosTableViewCell
cell.selectionStyle = .none
cell.photoFromTableView = //Image you want to pass
return cell
}
Assuming you have multiple UICollectionViewCells
In OtherUserPhotosTableViewCell.swift
var photosArray: [UIImage]?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "otherUserPhotosCollectionCell", for: indexPath) as! OtherUserPhotosCollectionViewCell
//Configure cell with photo from parent ViewController
if let photo_array:[UIImage] = photosArray {
cell.imageView.image = photo_array[IndexPath.row]
}
return cell
}
In OtherUserAccountViewController.swift
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:OtherUserPhotosTableViewCell = self.tableView.dequeueReusableCell(withIdentifier: photosCellReuseIdentifier) as! OtherUserPhotosTableViewCell
cell.selectionStyle = .none
cell.photosArray = //Array of Images you want to pass
return cell
}
I have a UICollectionView with custom cells. Everything is working correctly, but what I do not know how to do is assign different text to the labels in each cell. I have one section and three items in each section. I want all three items to have the exact same layout. My thoughts are I need to assign a different identifier for each cell, but I am unsure how I set different text for more than just one cell identifier.
Any help is appreciated, thank you.
let reuseIdentifier = "directionCell"
override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 3
}
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
cell.titleLabel.text = "Current title"
cell.subtitleLabel.text = "Choose to start"
cell.layer.cornerRadius = 10
return cell
If you have 3 cells and 3 cells has different content, just create 3 collectionviewcels and connect IB outlets and then called theme like this :-
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
var cell: UICollectionViewCell!
let reuseIdentifier = "CustomCollectionViewCell"
let cell1 = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
if indexPath.row == 0 {
cell1.titleLabel.text = "Current title"
cell1.subtitleLabel.text = "Choose to start"
cell1.layer.cornerRadius = 10
cell = cell1
} else if indexPath.row == 1 {
cell2.titleLabel.text = "Current title"
cell2.subtitleLabel.text = "Choose to start"
cell2.layer.cornerRadius = 10
cell = cell2
} else {
cell3.titleLabel.text = "Current title"
cell3.subtitleLabel.text = "Choose to start"
cell3.layer.cornerRadius = 10
cell = cell3
}
return cell
}
When you set the numberOfItemsInSection to 3, cellForItemAtIndexPath will also be called 3 times. So, if you have an array of data or information that you want to be displayed then use it in cellForItemAtIndexPath function to get the result that you want. So, assuming you have an array of titles, use that array in
the cellForItemAtIndexPath.
var titles: [String] = ["Title1", "Title2", "Title3"]
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
{
let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as! CustomCollectionViewCell
cell.titleLabel.text = self.titles[indexPath.row]
cell.subtitleLabel.text = "Choose to start"
cell.layer.cornerRadius = 10
return cell
}