I am trying to create different cells for each index item in my collectionView and I am attempting to downcast them as their respective cell in order to access their variables.
The code below outputs this error "Cannot assign value of type 'LiveCell.Type' to type 'UICollectionViewCell'"
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
let cellName: UICollectionViewCell
if indexPath.item == 1 {
identifier = LiveCellId
cellName = LiveCell
} else if indexPath.item == 2 {
identifier = LiveCellId
cellName = LiveCell
} else {
identifier = FeedCellId
cellName = FeedCell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! cellName //Trying to downcast cell from the variable called cellName
cell.homeController = self
return cell
}
Is there a solution for this. Thanks in advance
Try This:-
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
let cellName: UICollectionViewCell?
if indexPath.item == 1 {
identifier = LiveCellId
cellName = LiveCell as? UICollectionViewCell
} else if indexPath.item == 2 {
identifier = LiveCellId
cellName = LiveCell as? UICollectionViewCell
} else {
identifier = FeedCellId
cellName = FeedCell as? UICollectionViewCell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! cellName //Trying to downcast cell from the variable called cellName
cell.homeController = self
return cell
}
You can do something like this:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let identifier: String
if indexPath.item == 1 || indexPath.item == 2 {
identifier = LiveCellId
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! LiveCell
cell.homeController = self
return cell
} else {
identifier = FeedCellId
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: identifier, for: indexPath) as! FeedCell
cell.homeController = self
return cell
}
}
Related
[ I want to use two custom cells in one UICollectionView and this is by far I have been trying and I am stuck. Cell one for first and row and cell two of the second row and again cell one for 3rd, you get the point.
here is the code ]
import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
#IBOutlet weak var CV: UICollectionView!
let labelArray = ["one", "two", "three", "four"]
let imageArray = [UIImage(named: "one"), UIImage(named: "two"), UIImage(named: "three"), UIImage(named: "four") ]
override func viewDidLoad() {
super.viewDidLoad()
CV.delegate = self
CV.dataSource = self
CV.reloadData()
// let nibCellOne = UINib(nibName: "CVCellOne", bundle: nil)
// let nibCellTwo = UINib(nibName: "CVCellTwo", bundle: nil)
// CV.register(nibCellOne, forCellWithReuseIdentifier: "CVCellOne")
// CV.register(nibCellTwo, forCellWithReuseIdentifier: "CVCellTwo")
CV.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CVCellOne")
CV.register(CollectionViewCell.self, forCellWithReuseIdentifier: "CVCellTwo")
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 4
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCellOne", for: indexPath) as! CollectionViewCell
cell1.cellOneLabel.text = labelArray[indexPath.row]
return cell1
}
else if indexPath.section == 1 {
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCellTwo", for: indexPath) as! CollectionViewCell
cell2.cellTwoLabel.text = labelArray[indexPath.row]
return cell2
} else if indexPath.section == 2 {
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCellOne", for: indexPath) as! CollectionViewCell
cell1.cellOneImage.image = imageArray[indexPath.row]
cell1.cellOneLabel.text = labelArray[indexPath.row]
return cell1
} else {
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCellTwo", for: indexPath) as! CollectionViewCell
cell2.cellTwoImage.image = imageArray[indexPath.row]
cell2.cellTwoLabel.text = labelArray[indexPath.row]
return cell2
}
}
}
This is the customCell xib file
If what you need is every even row to use Cell1, and every odd row to use Cell2, then don't check for the specific row, check for the remainder when you divide by 2 (modulus) indexPath.item % 2 == 0
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item % 2 == 0 {
let cell1 = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCellOne", for: indexPath) as! CollectionViewCell
cell1.cellOneLabel.text = labelArray[indexPath.row]
return cell1
}
} else {
let cell2 = collectionView.dequeueReusableCell(withReuseIdentifier: "CVCellTwo", for: indexPath) as! CollectionViewCell
cell2.cellTwoLabel.text = labelArray[indexPath.row]
return cell1
}
}
}
I have a UICollectionView with 4 UICollectionViewCells. Each cell contains a UITableView with a varying number of cells.
I have an array from which I need to pass a value (depending on the indexPath) into the cell. My problem is that it always gives me nil when I try to do this.
So for example I have this array:
let arrayOfStrings = ["test1", "test2", "test3", "test4"]
I have this code in my collection view's cellForItemAt method:
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "testCollectionViewCell", for: indexPath) as! TestCollectionViewCell
cell.myString = arrayOfStrings[indexPath.row]
cell.label.text = "This text is set."
return cell
}
The cell's label is set properly, but when I try to print myString in the cell's table view's cellForRowAt method it always returns nil.
I need the string because I plan to manipulate the table views depending on the string passed in.
So why is this happening and what can be done?
You can try like this
extension HomeViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 4
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "DealTableViewCell", for: indexPath) as! DealTableViewCell
cell.DealCollectionView.dataSource = self
cell.DealCollectionView.delegate = self
cell.DealCollectionView.tag = 10101
return cell
} else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "BusinessTableViewCell", for: indexPath) as! BusinessTableViewCell
cell.BusinessCollectionView.dataSource = self
cell.BusinessCollectionView.delegate = self
cell.BusinessCollectionView.tag = 10102
return cell
} else if indexPath.row == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "FilterTableViewCell", for: indexPath) as! FilterTableViewCell
cell.FilterCollectionView.dataSource = self
cell.FilterCollectionView.delegate = self
cell.FilterCollectionView.tag = 10103
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "ComboTableViewCell", for: indexPath) as! ComboTableViewCell
cell.ComboCollectionView.dataSource = self
cell.ComboCollectionView.delegate = self
cell.ComboCollectionView.tag = 10104
return cell
}
}
}
Add the collection view to each tableview cell and set delegate in tableview cell
extension HomeViewController : UICollectionViewDelegate, UICollectionViewDataSource {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if collectionView.tag == 10101 {
return 3
} else if collectionView.tag == 10102 {
return 3
} else if collectionView.tag == 10103 {
return 3
} else if collectionView.tag == 10104 {
return 3
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if collectionView.tag == 10101 {
let cell = collectionView.dequeueReusableCell(withIdentifier: "DealCollectionViewCell", for: indexPath) as! DealCollectionViewCell
return cell
} else if collectionView.tag == 10102 {
let cell = collectionView.dequeueReusableCell(withIdentifier: "BusinessCollectionViewCell", for: indexPath) as! BusinessCollectionViewCell
return cell
} else if collectionView.tag == 10103 {
let cell = collectionView.dequeueReusableCell(withIdentifier: "FilterCollectionViewCell", for: indexPath) as! FilterCollectionViewCell
return cell
} else if collectionView.tag == 10104 {
let cell = collectionView.dequeueReusableCell(withIdentifier: "ComboCollectionViewCell", for: indexPath) as! ComboCollectionViewCell
return cell
}
return UICollectionViewCell()
}
}
For reload the collectionView in use
if let cell = self.tableView.cellForRow(at: IndexPath(row: i, section: 0)) as?
tableViewCell {
cell.collectionView.reloadData()
}
I have two custom UICollectionViewCells(AddImageCollectionViewCell, ItemCollectionViewCell) which I am loading depending on indexpath. Here is my code for the cellForItemAtIndexpath-
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
var cell :UICollectionViewCell!
if indexPath.row == 0{
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "addImageCell", for: indexPath) as! AddImageCollectionViewCell
}
else{
cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCollectionViewCell
cell.contentImage = self.droppedItemList[indexPath.row] //error here
}
return cell
}
I am getting the error as "Value of type 'UICollectionViewCell?' has no member 'contentImage'". Why is my cell in the else clause is not cast to "ItemCollectionViewCell" type.
I know, I must be doing something very foolish. I would be really grateful if someone can point me to the right direction.
You are declaring cell as basic type UICollectionViewCell, that's the reason. Return the cells separately
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.row == 0 {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "addImageCell", for: indexPath) as! AddImageCollectionViewCell
return cell
} else {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "ItemCell", for: indexPath) as! ItemCollectionViewCell
cell.contentImage = self.droppedItemList[indexPath.row]
return cell
}
}
based on the screen shot, I will have a big collectionview to contain few cells (with colors). All cell will display only one time in the view except for the green one.The green one will display an array of users.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.item == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: topfeatureCellIndent, for: indexPath) as! topFeatureCell
//configure if needed
return cell
}else if indexPath.item == 1{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: userCellIdent, for: indexPath) as! featureUserContainerViewCell
cell.featureUsers = featureUser
cell.selectUserdelegate = self
return cell
}else if indexPath.item == 2{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ticketLabelIdent, for: indexPath) as! ticketLabelCell
return cell
} else if indexPath.item == 3{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: whosgoingIdent, for: indexPath) as! whoGoingCell
cell.config(withTimer: timeleft)
return cell
}
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: allUserCellIdent, for: indexPath) as! allUserCell
let index = indexPath.item - 4
let user = allPartyUserArr![index]
cell.config(withUser: user)
return cell
The way I need to display the last cell is by implement the code above but I think its not correct, because what if I want to add in other cells after displaying all the cells, is there any better way to dequeue the cell properly?
I would suggest you to use 2 Sections in UICollectionView.
keep all the one-time visible cells in section 0 and the cells which will represent array for users in section 1
This is how you can set number of sections
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 2
}
To set Header of each section you can implement below functions and set any size for your header. CGSizeMake(0, 0) will hide the Header
func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width : 0, height : 0) // Header size
}
then number of items in each section
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
if section == 0 {
return 4
}
else {
users.count
}
//return (section == 0) ? 4 : users.count
}
to display cell
public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
if indexPath.section == 0 {
// Based on Your implementation
if indexPath.item == 0{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: topfeatureCellIndent, for: indexPath) as! topFeatureCell
//configure if needed
return cell
}else if indexPath.item == 1{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: userCellIdent, for: indexPath) as! featureUserContainerViewCell
cell.featureUsers = featureUser
cell.selectUserdelegate = self
return cell
}else if indexPath.item == 2{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ticketLabelIdent, for: indexPath) as! ticketLabelCell
return cell
} else if indexPath.item == 3{
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: whosgoingIdent, for: indexPath) as! whoGoingCell
cell.config(withTimer: timeleft)
return cell
} else {
return UICollectionViewCell()
}
}else{
//make sure the identifier of your cell for second section
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: allUserCellIdent, for: indexPath) as! allUserCell
// populate your user cell here
return cell
}
}
You can set the number of sections in CollectionView like this :
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 5
}
Set number of items in each section :
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
switch section {
case 0:
return 1
default:
return 3
}
}
Number of items you can set according to the section.
and for cells in each section :
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
switch indexPath.section {
case 0:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: topfeatureCellIndent, for: indexPath) as! topFeatureCell
//configure if needed
return cell
case 1:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: userCellIdent, for: indexPath) as! featureUserContainerViewCell
cell.featureUsers = featureUser
cell.selectUserdelegate = self
return cell
case 2:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ticketLabelIdent, for: indexPath) as! ticketLabelCell
return cell
case 3:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: topfeatureCellIndent, for: indexPath) as! topFeatureCell
//configure if needed
return cell
case 4:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: whosgoingIdent, for: indexPath) as! whoGoingCell
cell.config(withTimer: timeleft)
return cell
default:
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: allUserCellIdent, for: indexPath) as! allUserCell
let index = indexPath.item - 4
let user = allPartyUserArr![index]
cell.config(withUser: user)
return cell
}
}
Hope this helps or else you can google for more better tutorials and solutions.
I want to add a cell into my tableview to the last row using another UserInterface.The code is like this.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell:JoinCell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! JoinCell
let lastSectionIndex = tableView.numberOfSections-1
let lastSectionLastRow = tableView.numberOfRowsInSection(lastSectionIndex) - 1
let lastIndexPath = NSIndexPath(forRow:lastSectionLastRow, inSection: lastSectionIndex)
let cellIndexPath = tableView.indexPathForCell(cell)
if cellIndexPath == lastIndexPath {
cell = tableView.dequeueReusableCellWithIdentifier("JoinFooterCell") as! JoinFooterCell
}
I got error message "Cannot assign value of type 'JoinFooterCell' to type 'JoinCell'"
Does anyone can give me advise? Thanks.
Do this and for datasource.sections.count use the same value you return in numberOfSectionInTableView() and for datasource.rows.count use the same value you return in your numberOfRowsForSection()
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if datasource.sections.count - 1 == indexPath.section && datasource.rows.count - 1 == indexPath.row {
let cell = tableView.dequeueReusableCellWithIdentifier("JoinFooterCell") as! JoinFooterCell
return cell
} else {
let cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! JoinCell
return cell
}
}
Do something like this
let lastSectionIndex = tableView.numberOfSections-1
let lastSectionLastRow = tableView.numberOfRowsInSection(lastSectionIndex) - 1
let lastIndexPath = NSIndexPath(forRow:lastSectionLastRow, inSection: lastSectionIndex)
let cellIndexPath = tableView.indexPathForCell(cell)
var cell
if cellIndexPath == lastIndexPath {
cell = tableView.dequeueReusableCellWithIdentifier("JoinFooterCell") as! JoinFooterCell
}
else {
cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! JoinCell
}
What actually am trying to do is create cell which you want to return. Don't initialize the cell first and then try to re assign. First determine whether its the last cell and based on that, initialize the kind of cell you want.
Let try this:
var cell: UITableViewCell!
if cellIndexPath == lastIndexPath {
cell = tableView.dequeueReusableCellWithIdentifier("JoinFooterCell") as! JoinFooterCell
} else {
cell = tableView.dequeueReusableCellWithIdentifier(reuseIdentifier, forIndexPath: indexPath) as! JoinCell
}
return cell