I have been trying to use static cells using XIB's ,But in cell for row at index path I am getting an error like "Cannot convert return expression of type 'UITableViewCell.Type' to return type 'UITableViewCell'" at the line of " return UITableViewCell".Can any one help me to do this ,Thanks in advance.
extension TriipDetailsViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell : TripDriverDetailsCell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripDriverDetailsCell", for: indexPath) as! TripDriverDetailsCell
return cell
}else if indexPath.row == 1 {
let cell : TripFareDetailsCell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripFareDetailsCell", for: indexPath) as! TripFareDetailsCell
return cell
}else if indexPath.row == 2 {
let cell : TripPaymentModeCell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripPaymentModeCell", for: indexPath) as! TripPaymentModeCell
return cell
}
return UITableViewCell
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath.row == 0{
return 230
}else if indexPath.row == 1{
return 200
}else if indexPath.row == 2{
return 120
}
}
}
Eliminate the return UITableViewCell.
Some people might say return UITableViewCell(), but I’d rather handle this case as the error that it really is, e.g.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.row {
case 0:
return tableView.dequeueReusableCell(withIdentifier: "TripDriverDetailsCell", for: indexPath)
case 1:
return tableView.dequeueReusableCell(withIdentifier: "TripFareDetailsCell", for: indexPath)
case 2:
return tableView.dequeueReusableCell(withIdentifier: "TripPaymentModeCell", for: indexPath)
default:
fatalError("Unexpected row number \(indexPath.row)")
}
}
The heightForRowAt should similarly handle the scenario where the indexPath.row is not 0, 1, or 2, e.g.:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
switch indexPath.row {
case 0: return 230
case 1: return 200
case 2: return 120
default: fatalError("Unexpected row number \(indexPath.row)")
}
}
let cell : UITableViewCell
if indexPath.row == 0
{
cell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripDriverDetailsCell", for: indexPath) as! TripDriverDetailsCell
}else if indexPath.row == 1 {
cell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripFareDetailsCell", for: indexPath) as! TripFareDetailsCell
}else if indexPath.row == 2 {
cell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripPaymentModeCell", for: indexPath) as! TripPaymentModeCell
}else {
cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath)
}
return cell
}
You've got
Cannot convert return expression of type 'UITableViewCell.Type' to return type 'UITableViewCell
because you return a cell as return UITableViewCell that means you return a whole UITableViewCell class instead of single UITableViewCell() therefore in the last you've to return cell as return UITableViewCell()
e.g.
extension TriipDetailsViewController: UITableViewDelegate,UITableViewDataSource{
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0
{
let cell : TripDriverDetailsCell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripDriverDetailsCell", for: indexPath) as! TripDriverDetailsCell
return cell
}else if indexPath.row == 1 {
let cell : TripFareDetailsCell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripFareDetailsCell", for: indexPath) as! TripFareDetailsCell
return cell
}else if indexPath.row == 2 {
let cell : TripPaymentModeCell = tripDetailsTableView.dequeueReusableCell(withIdentifier: "TripPaymentModeCell", for: indexPath) as! TripPaymentModeCell
return cell
}
return UITableViewCell() //Here you've to change
}
Related
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 am creating a TableView that starts with an image at the top, approximately 5 cells of parsed json, another image, approximately 3 more cells of parsed json, and another image.
I have 3 custom nibs that I am using. Each one is unique.
I get an "Index out of range" error on the line "let item = page?.collapsibles[indexForSecondSetTableViewCells]"
Here is my code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentIndex = indexPath.row - 1
let numberofCarousels = self.page?.carousels.count
let indexAfterCarousels = numberofCarousels ?? 00 + 1
let indexForSecondSetTableViewCells = indexAfterCarousels + 1
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "pipesOne", for: indexPath) as! Pipes1TableViewCell
cell.pipesOne.image = UIImage(named: "WhatsNew1")
return cell
}
if indexPath.row == indexAfterCarousels {
let cell = tableView.dequeueReusableCell(withIdentifier: "pipesOne", for: indexPath) as! Pipes1TableViewCell
cell.pipesOne.image = UIImage(named: "WhatsNew2")
return cell
}
if indexPath.row == indexForSecondSetTableViewCells {
let cell = tableView.dequeueReusableCell(withIdentifier: "collapsibleCell", for: indexPath) as! CollapsiblesTableViewCell
let item = page?.collabsible[indexForSecondSetTableViewCells]
cell.collabsibleTitle.text = item?.title
cell.collabsibleDescription.text = item?.content
return cell
}
let cell = tableView.dequeueReusableCell(withIdentifier: "newsTableViewCell", for: indexPath) as! NewsTableViewCell
let item = page?.carousels[currentIndex]
cell.newsTitle.text = item?.title
cell.newsText.text = item?.caption
cell.newsImage.kf.setImage(with: page?.carousels[currentIndex].imageURL)
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return (page?.carousels.count ?? 0) + 1 + (page?.collapsibles.count ?? 0)
}
Update: I simplified the code to use the same json object twice and so that each item maps to exactly the cell that it should go to and I still get the "Fatal error: Index out of range" error at row 7 with let item = page?.collapsible[indexPath.row]
New code:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "pipesOne", for: indexPath) as! Pipes1TableViewCell
cell.pipesOne.image = UIImage(named: "WhatsNew1")
return cell
}
if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "newsTableViewCell", for: indexPath) as! NewsTableViewCell
let item = page?.carousels[indexPath.row]
cell.newsTitle.text = item?.title
cell.newsText.text = item?.caption
cell.newsImage.kf.setImage(with: page?.carousels[indexPath.row].imageURL)
return cell
}
if indexPath.row == 6 {
let cell = tableView.dequeueReusableCell(withIdentifier: "pipesOne", for: indexPath) as! Pipes1TableViewCell
cell.pipesOne.image = UIImage(named: "WhatsNew2")
return cell
}
if indexPath.row == 7 {
let cell = tableView.dequeueReusableCell(withIdentifier: "collapsibleCell", for: indexPath) as! CollapsiblesTableViewCell
let item = page?.collapsibles[indexPath.row]
cell.collabsibleTitle.text = item?.title
cell.collabsibleDescription.text = item?.content
return cell
}
return UITableViewCell()
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 11
}
Try to print("\(indexPath)") in
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("\(indexPath)")
}
Then you will know how many rows it successfully shows before crash.
Then in cellForRowAt put conditional break point such it stops when indexPath is the one it crashed.
Now do po (page?.carousels.count ?? 0) + 1 + (page?.collapsibles.count ?? 0)
to see how many rows your tableview has. Most definitely indexForSecondSetTableViewCells is more than the number of rows
var mutipleArrayForFirstCell:[String] = ["1","2","3","4","5","6","7","8","9","10"]
var mutipleArrayForSecondCell:[String] = ["11","12","13","14","15","16","17","18","19","20"]
//MARK - TableView delegates
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 1 {
return mutipleArrayForFirstCell.count
}else {
return mutipleArrayForSecondCell.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let valueAt1stCell = tableView.dequeueReusableCell(withIdentifier: "FirstCell", for: indexPath) as! multipleCustomCellTableViewCell
valueAt1stCell.Firstlabel.text = mutipleArrayForFirstCell[indexPath.row]
return valueAt1stCell
} else {
let valueAt2ndCell = tableView.dequeueReusableCell(withIdentifier: "SecondCell", for: indexPath) as! SecondCustomTableviewCell
valueAt2ndCell.secondLabel.text = mutipleArrayForSecondCell[indexPath.row]
return valueAt2ndCell
}
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 100
}
My questions is i want to show the above declared two array value in two different cells. How can i do it?? it showing like this:
But it should show 1-10 in one cell and 11-20 in another cell.someone please help me ,Thanks in advance
how can i give some space between two cells ?
Try this one
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
//First Array
let valueAt1stCell = tableView.dequeueReusableCell(withIdentifier: "FirstCell", for: indexPath) as! multipleCustomCellTableViewCell
valueAt1stCell.Firstlabel.text = mutipleArrayForFirstCell[indexPath.row]
return valueAt1stCell
default:
//Second Array
let valueAt2ndCell = tableView.dequeueReusableCell(withIdentifier: "SecondCell", for: indexPath) as! SecondCustomTableviewCell
valueAt2ndCell.secondLabel.text = mutipleArrayForSecondCell[indexPath.row]
return valueAt2ndCell
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let valueAt1stCell = tableView.dequeueReusableCell(withIdentifier: "FirstCell", for: indexPath) as! multipleCustomCellTableViewCell
valueAt1stCell.Firstlabel.text = mutipleArrayForFirstCell[indexPath.row]
return valueAt1stCell
} else {
let valueAt2ndCell = tableView.dequeueReusableCell(withIdentifier: "SecondCell", for: indexPath) as! SecondCustomTableviewCell
valueAt2ndCell.secondLabel.text = mutipleArrayForSecondCell[indexPath.row]
return valueAt2ndCell
}
}
I try to use UITableViewCell's accessoryType property to checkmark cell when clicked ,but when cell selected , the checkmark set several time for different cells for example when I select row [0] , row [0] and row [8] and row [17] AccessoryType set to checkmark !
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.selectionStyle = .none
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)
cell?.accessoryType = .checkmark
tableView.reloadData()
}
For single selection, you need to track your selected indexPath in a viewController variable,
var selectedIndexPath : IndexPath?
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndexPath = indexPath
tableView.reloadData()
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.accessoryType = .none
cell.selectionStyle = .none
if(indexPath == selectedIndexPath) {
cell.accessoryType = .checkmark
}
return cell
}
Even better (avoiding reload the entire UITableView)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let previousSelectedIndexPath = self.selectedIndexPath
self.selectedIndexPath = indexPath
if(previousSelectedIndexPath != nil) {
self.tableView.reloadRows(at: [previousSelectedIndexPath!,self.selectedIndexPath!], with: .automatic)
}else{
self.tableView.reloadRows(at: [self.selectedIndexPath!], with: .automatic)
}
self.tableView.reloadData()
}
UPDATE, Allowing multiple selection
For multiple selection you should track selected cells in a Dictionary for convenience faster access to selected and unselected indexPaths allowing you use multiple sections because the key value of our Dictionary is a string formed by (IndexPath.section)+(IndexPath.row) which is always unique combination
var selectedIndexPaths : [String:Bool] = [:]
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let currentIndexPathStr = "\(indexPath.section)\(indexPath.row)"
if(self.selectedIndexPaths[currentIndexPathStr] == nil || !self.selectedIndexPaths[currentIndexPathStr]!) {
self.selectedIndexPaths[currentIndexPathStr] = true
}else{
self.selectedIndexPaths[currentIndexPathStr] = false
}
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "npCell", for: indexPath) as! NewPlaylistTableViewCell
cell.mTitle.text = musics[indexPath.row]["title"] as! String?
cell.mArtist.text = musics[indexPath.row]["artist"] as! String?
cell.accessoryType = .checkmark
let currentIndexPathStr = "\(indexPath.section)\(indexPath.row)"
if(self.selectedIndexPaths[currentIndexPathStr] == nil || !self.selectedIndexPaths[currentIndexPathStr]!) {
cell.accessoryType = .none
}
return cell
}
Results
I am trying to use a UITableViewController(Static table view) with 4 sections and each has a different type of cell.
The number of cells in a section depends on the data received from the web service. In the storyboard, I have created one type of cell in each section.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Number of rows in attachment sect
if section == 2 {
guard let currentDataModel = dataModel else { return 0 }
return currentDataModel.attachments.count
}
else if section == 3 {
guard let currentDataModel = dataModel else { return 0 }
return currentDataModel.contacts.count
}
return super.tableView(tableView, numberOfRowsInSection: section)
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let currentSection = indexPath.section
if currentSection == 2 {
let currentRow = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "attachmentCell", for: indexPath) as! AttachmentViewCell
cell.setDataForCell(dataModel: dataModel?.attachments[currentRow], indexPath: indexPath)
return cell
}
//Show cells for Contact Section
else if currentSection == 3 {
let currentRow = indexPath.row
let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactViewCell
cell.setDataForCell(dataModel: dataModel?.contacts[currentRow], indexPath: indexPath)
return cell
}
return super.tableView(tableView, cellForRowAt: indexPath)
}
I am getting this error while calling dequeueReusableCellWithIdentifier:forIndexPath:
Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds [0 .. 1]'
Can I Use the different type of cells and a different number of rows in each section in a UITableViewController?
Can you try this.If need a four sections then return 4 in numberofsections. You can add the title to each sections as below and later add it to label in viewForHeaderInSection.You must provide the number of rows in each sections correctly.
override func numberOfSectionsInTableView(tableView: UITableView) -> Int
{
return 4
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if(section == 0)
{
return "Section 0"
}
else if(section == 1)
{
return "Section 1"
}
else if(section == 2)
{
return "section 2"
}
else if(section == 3)
{
return "section 3"
}
return ""
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
switch(section)
{
case 0: //for first section
//return your count
case 1://Cells for
//return your count
case 2:
//return your count
case 3:
//return your count
default:
return 4
}
return 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! Cell1
return cell
}
else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! Cell1
return cell
}
else if indexPath.section == 2 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! Cell1
return cell
}
else if indexPath.section == 3 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! Cell1
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as? Cell2
//Set up labels etc.
return cell!
}
}
override func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 50))
headerView.layer.borderWidth = 2
let myLabel = UILabel()
myLabel.frame = CGRectMake(0, 0, tableView.frame.width - 70, 50)
myLabel.font = UIFont.boldSystemFontOfSize(18)
myLabel.textColor = UIColor.whiteColor()
myLabel.textAlignment = .Left
myLabel.text = " " + self.tableView(tableView, titleForHeaderInSection: section)!
headerView.addSubview(myLabel)
return headerView
}
If you are using .xib file then you should also register that file in viewDidLoad() as
self.tableView.registerNib(UINib(nibName: "cell1", bundle: nil), forCellReuseIdentifier: "cell1")
self.tableView.registerNib(UINib(nibName: "cell2", bundle: nil), forCellReuseIdentifier: "cell2")