Essentially I have a tableview with two types of cells. One is a cell that contains a number that is being pulled from an array. The second cell is just supposed to act as a spacing cell but can contain information. However, I've only seen solutions that have the second cell come after the first if it its row is equal to 0 or if it is sectioned off.
The array of numbers is [1,2,3,4,5,6,7] and the first cell type pulls from it to display each number
I'm trying to get something that looks like this:
celltypeone: 1
celltypetwo
celltypeone: 2
celltypetwo
celltypeone: 3
celltypetwo
celltypeone: 4
celltypetwo
... and so on.
I thought about trying to get the space cell to come after every next cell but I didn't think that made sense.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if index >= 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "productsblock", for: indexPath ) as! DetailTBCell
return cell
}
else {
let spacecell = tableView.dequeueReusableCell(withIdentifier: "spacingcell", for: indexPath ) as! DetailSpaceTBCell
return spacecell
}
}
This prints all the numbers from the array and displays them but the second cell does not show at all.
I'm unsure of what to do or how to get it to work.
if (index % 2) == 0 | try if index number is double put the first tableCell else put the other one
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if (index % 2) == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "productsblock", for: indexPath ) as! DetailTBCell
return cell
}
else {
let spacecell = tableView.dequeueReusableCell(withIdentifier: "spacingcell", for: indexPath ) as! DetailSpaceTBCell
return spacecell
}
If you notice your product cell needs to be shown at even position and the spacing cell at odd position.
`index >= 0 ` needs to replaced with `index % 2 == 0 `
Moreover in order to fetch data from product array you will be needing to divide the index by 2.
let products = [1,2,3,4,5,6,7]
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if index % 2 == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "productsblock", for: indexPath ) as! DetailTBCell
// Fetching data from array products defined above
let data = products[index / 2]
return cell
}
else {
let spacecell = tableView.dequeueReusableCell(withIdentifier: "spacingcell", for: indexPath ) as! DetailSpaceTBCell
return spacecell
}
Related
I created a score board using table view and I want to highlight the first row of the table. When I run the app, the first row is highlighted as expected but when I scroll the table, several cell is highlighted instead of first cell.
Here is my code;
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "score", for: indexPath) as! ScoreTableViewCell
if indexPath.row == 0 {
cell.backgroundImageView.image = UIImage(named: "firstClass")
}
cell.nicknameLabel.text = self.sortedResults[indexPath.row]["nickname"] as? String
cell.scoreLabel.text = " \(indexPath.row + 1)"
return cell
}
Cells are dequeued meaning when your cellForRow is asked for cell ( that's not the first one ) the returned cell may dequeue that first cell so make sure to set nil when it's not index zero by replacing
if indexPath.row == 0 {
cell.backgroundImageView.image = UIImage(named: "firstClass")
}
with
cell.backgroundImageView.image = indexPath.row == 0 ? UIImage(named: "firstClass") : nil
I'm using two TableViews in a ViewController but I get this error when it gets to the second TableViewCell, cartProductCell. They both have custom classes, and outlets, as it was the problem for many in other posts. Is the first time I'm doing this and I can't find a solution for this. May it be just because I'm using custom classes for the cells? In the tutorials I found about two TableViews weren't used custom classes.
In the Storyboard editor everything is connected well and identifiers are both correct.
Here's the function:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
// if tableView == self.worksTableView && CartViewController.bookedWoksArray.count > 0 {
if tableView == self.worksTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartWorkCell", for: indexPath as IndexPath) as! CartWorkTableViewCell
cell.workImageView.image = CartViewController.bookedWoksArray[indexPath.row].0
cell.workNameLabel.text = CartViewController.bookedProductsArray[indexPath.row].1
cell.workPriceLabel.text = CartViewController.bookedWoksArray[indexPath.row].2
} // else {return}
// else if tableView == self.worksTableView && CartViewController.bookedProductsArray.count > 0 {
if tableView == self.worksTableView {
let cell = tableView.dequeueReusableCell(withIdentifier: "cartProductCell", for: indexPath as IndexPath) as! CartProductTableViewCell
cell.cartProductImageView.image = CartViewController.bookedProductsArray[indexPath.row].0
cell.cartProductNameLabel.text = CartViewController.bookedProductsArray[indexPath.row].1
cell.cartProductPriceLabel.text = CartViewController.bookedProductsArray[indexPath.row].2
} //else {return}
return cell!
}
As usual many thanks
After a few tries and after fixing a silly mistake I finally made it work by assigning the value of custom cells to cell and the function's code is now:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell:UITableViewCell?
if tableView == self.worksTableView {
let workCell = tableView.dequeueReusableCell(withIdentifier: "cartWorkCell", for: indexPath) as! CartWorkTableViewCell
workCell.workImageView.image = CartViewController.bookedWoksArray[indexPath.row].0
workCell.workNameLabel.text = CartViewController.bookedWoksArray[indexPath.row].1
workCell.workPriceLabel.text = CartViewController.bookedWoksArray[indexPath.row].2
cell = workCell
}
if tableView == self.productsTableView{
let productCell = tableView.dequeueReusableCell(withIdentifier: "cartProductCell", for: indexPath) as! CartProductTableViewCell
productCell.cartProductImageView.image = CartViewController.bookedProductsArray[indexPath.row].0
productCell.cartProductNameLabel.text = CartViewController.bookedProductsArray[indexPath.row].1
productCell.cartProductPriceLabel.text = CartViewController.bookedProductsArray[indexPath.row].2
cell = productCell
}
return cell!
}
I add a cell with a label in it in a new section on the top of the tableView as section 0 and i show and hide this section according to what type of data i'm displaying.
It works fine when there is no data in the hashtag type posts then when there is hashtag data to be displayed in the array like two or three items it works fine and the top section 0 cell is displayed then when i scroll down and up again i get an error in the AppDelegate after trying to return the top section cell.
I know the question is a little bit complicated but what i'm trying to achieve is to display and hide a cell on the top of my feed according to the type of data i'm displaying in my tableview. If hashtag news feed data then show the top cell in section 0 if showing ordinary news feed in the tableview then return only one section and don't load the top section with the cell inside of it.
By the way i'm displaying the cell as a Nib. And declaring it in the viewDidLoad
let reloadNib = UINib(nibName: "ReloadTableViewCell", bundle: nil)
feedTableView.register(reloadNib, forCellReuseIdentifier: "reloadCell")
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x102a772a0)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
switch indexPath.section {
case 0:
if hashPostsOnly {
let reloadCell = tableView.dequeueReusableCell(withIdentifier: "reloadCell", for: indexPath) as! ReloadTableViewCell
return reloadCell // ERROR AFTER RETURNING CELL
} else {
//For the protocol delegate i made
cell.delegate = self
cell.feed = feeds[indexPath.row]
cell.postCommentTextView.tag = indexPath.row
cell.cellIndexPath = indexPath
cell.userProfilePhotoBtn.tag = indexPath.row
cell.postMoreCommentsBtn.tag = indexPath.row
cell.postMoreCommentsBtn.addTarget(self, action: #selector(moreCommentsTapped(_:)), for: .touchUpInside)
return cell
}
case 1:
//For the protocol delegate i made
cell.delegate = self
cell.feed = feeds[indexPath.row]
cell.postCommentTextView.tag = indexPath.row
cell.cellIndexPath = indexPath
cell.userProfilePhotoBtn.tag = indexPath.row
cell.postMoreCommentsBtn.tag = indexPath.row
cell.postMoreCommentsBtn.addTarget(self, action: #selector(moreCommentsTapped(_:)), for: .touchUpInside)
return cell
default:
return cell
}
func numberOfSections(in tableView: UITableView) -> Int {
if hashPostsOnly {
return 2
} else {
return 1
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
if hashPostsOnly {
return 1
} else {
return feeds.count
}
} else {
return feeds.count
}
}
Here is a screen shot of what i'm achieving but when i scroll down then up it reloads the top section cell "Reload Feeds" and then error.
Since there are just two sections and you had duplicate code, things can be simplified to:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
if indexPath.section == 0 && hashPostsOnly
{
let reloadCell = tableView.dequeueReusableCell(withIdentifier: "reloadCell", for: indexPath) as! ReloadTableViewCell
return reloadCell
}
else
{
let cell = tableView.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! PostTableViewCell
//For the protocol delegate i made
cell.delegate = self
cell.feed = feeds[indexPath.row]
cell.postCommentTextView.tag = indexPath.row
cell.cellIndexPath = indexPath
cell.userProfilePhotoBtn.tag = indexPath.row
cell.postMoreCommentsBtn.tag = indexPath.row
cell.postMoreCommentsBtn.addTarget(self, action: #selector(moreCommentsTapped(_:)), for: .touchUpInside)
return cell
}
}
I can't be 100% sure without knowing the exact error you're getting or knowing if there are other issues in the code elsewhere causing this, but:
As a general rule, dequeuing twice from a table view and returning a single cell does bad things in weird and mysterious ways. Refactor your code to only deuque a regular cell when you need it and not to do so when you're showing the refresh button
I am trying to populate a UITableView prototype cell with "Facebook status". It has images and videos depending on the response from Facebook server. But I am not able to populate it properly.
I will get the first image from the array. But when I scroll down UITableView and reach the top again, the image disappears. And when I print the value of i it exceeds the count of array soon after the tableview is loaded.
I am adding the edited code here.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var rows : Int = 0
if section < numberOfRowsAtSection.count{
rows = numberOfRowsAtSection[section]
print(section,rows)
}
return rows
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
print("Section,Row",indexPath.section, indexPath.row)
switch(combinedArray[indexPath.section][indexPath.row]){
case "photo" :
let cell = tableView.dequeueReusableCell(withIdentifier: "ImageDetail", for:indexPath) as! imageCell
if(im < imageDetails.count){
let imageUrl = imageDetails[im]
let url = URL(string:imageUrl)
let data = try? Data(contentsOf: url!)
cell.storyDetailsLbl1.text = photoStoryDetails[im]
print(imageDetails[im])
cell.images1.image = UIImage(data: data!)
}
im = im + 1
return cell
case "video" :
let cell1 = tableView.dequeueReusableCell(withIdentifier: "VideoCell", for: indexPath) as! videoPlayerCell
cell1.videoView.isHidden = false
if vd < videoURLs.count{
print(videoURLs.count)
let urls = videoURLs[vd]
cell1.videoPlayerItem = AVPlayerItem.init(url: urls)
cell1.videoLabel.text = videoStoryDetails[vd]
}
vd = vd + 1
return cell1
case "link":
let cell2 = tableView.dequeueReusableCell(withIdentifier: "LinkCell", for: indexPath) as! linkCell
if ln < linkDetails.count {
cell2.linkLbl.text = linkDetails[ln]
}
ln = ln + 1
return cell2
default:
let cell3 = tableView.dequeueReusableCell(withIdentifier: "StatusCell", for: indexPath) as! statusCell
if st < statusDetails.count{
cell3.statusLbl.text = statusDetails[st]
}
st = st + 1
return cell3
}
}
Tableviews deque already created cells and reuse them to enhance performance. So a cell with the identifier "ImageDetail" could be reused and populated with data from any of your case statements. This will cause oddities like images disappearing when they shouldn't.
One way to solve this is to ensure you are handling each cell property in every case. cell.storyDetailsLbl1.text isn't handled in the photo case. cell.images1.image isn't handled in the link case, etc.
Better would be to define a cell with a unique identifier for each one of your cases. i.e. PhotoCell, LinkCell, VideoCell, etc.
This question follows up from this: Use UICollectionViews to create dynamic and multiple features.
I am able to create a static cell which displays the name and image of the recipe similar like this app:
Where I am stuck is creating a dynamic row which changes based on the amount of data inside i.e. utensils or nutritional values like the image below:
I know how to display rows of data on tableView normally. But not sure how to embed it into a section inside a tableView. I attempted to add multiple prototype cells and assign them to a subclass of UITableViewCell's. Then I try to use if statements in my cellForRow but this isn't soling my issue.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FirstCell
//set the data here
cell.recipeTitle.text = recipe.name
cell.imageView.image = UIImage(named: "url")
return cell
}
else if indexPath.row == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell2", for: indexPath) as! SecondCell
//set the data here
return cell
}
else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell3", for: indexPath) as! ThirdCell
//set the data here
return cell
}
}
I have also looked at this demonstration: https://medium.com/ios-os-x-development/ios-how-to-build-a-table-view-with-multiple-cell-types-2df91a206429, which is near to what I want to achieve but I have found it quite difficult to adapt to it.
If someone could direct me on how best to approach this or a good example then I would really appreciate it.
First you can't have static cells and dynamic cells in the same tableView. So how do you work around that? Define each of the static cells in the sections they belong in as well as the dynamic cells in the sections they belong to. That, however doesn't look like what you are trying to do. You just want multiple sections in the same tableView, each section with a different list of data
To do this you will need the number of sections so use the tableView(_:numberOfSections:) function.
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
You can then(and probably should) give each of those sections a title by initializing an array with the titles in your tableViewController(assuming thats what you are using. It could also just be a tableView).
let headerTitles = ["Nutritional Values", "Utensils", "Ingredients"]
Then use the tableView(_:titleForHeaderInSection:)
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
if section < headerTitles.count {
return headerTitles[section]
}
return nil
}
Now you can start defining your rows by the sections.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell
if indexPath.section == 0 {
//Setup up the first row
if indexPath.row == 0 {
//I'm not sure where or how you defined First/SecondCell but this may not work depending on those two questions.
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! FirstCell
return cell
} else if indexPath.row == 1 {
let cell = Bundle.main.loadNibNamed("StaticCell", owner: self, options: nil)?.first as! StaticCell
return cell
}
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell1", for: indexPath) as! SecondCell
//setup cell1 n your storyboard to have a label with a tag # of 12(or whatever number you want to use)
//you also want an array with your utensil data accessible here
let label = cell.viewWithTag(12) as! UILabel
label.text = utensilNames[indexPath.row]
return cell
} else if indexPath.section == 2 {
let cellIngredients = tableView.dequeueReusableCell(withIdentifier: "Ingredients", for: indexPath)
tableView.deselectRow(at: indexPath, animated: true)
return cellIngreidents
}
cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
return cell
}
The point here is to use sections then rows to distribute your data.
Just to clarify Section 0 Row 0 - N would be where you're static rows are setup. I found it best to use XIB files subclassing TableViewCell.
Hope this helps.
EDIT So the way I'm looking at the "static" cells is in the first section the xib is the only put exactly where you tell it to be placed. In the example above the first section in the second cell is the