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
Related
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
}
my data set is like
var data = [["1"],["2","3"],["4"]]
and I want to show it in table view
which I am able to using
cell.textlabel?.text = data[indexPath.section][indexPath.row]
I have a button in navigation bar which when I click should increment all the sections row by 1 without changing anything in data
and all the newly created cell textfield should say "add here"
my cell for rowat
let cell = table.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textlabel?.text = data[indexPath.section][indexPath.row]
return cell
how should I change my cell for row at so that I don't get fatal error index out of range
Adding to #iOSArchitect.com 's answer,
You should check the index in cellForRowAt.
The reason you are getting index out of range exception is because your numberOfCell count increases but the cell's data source remains the same.
So,
let cell = table.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if indexPath.row < data[indexPath.section].count{
cell.textlabel?.text = data[indexPath.section][indexPath.row]
}else{
cell.textlabel?.text = "add here"
}
return cell
This may not be the precise code but I hope you get the logical answer out of this.
Declare a variable named extraRows and assign its initial value as 0 and add a property observer to it so that whenever it changes, it reloads the tableView
var extraRow : Int = 0 {
didSet {
self.tableView.reloadData()
}
}
After the click of the add button, update extraRow to + 1
#IBAction func addBtnClicked(_ sender : Any) {
extraRow = extraRow + 1
}
Also, update your number of rows
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count + extraRows
}
Along with this, you also need to update your tableview cellForRowAtIndexPath and place a check for if indexPath.row > data.count , then show your empty celll
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
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
every time I scroll up or down value get changed with wrong value at 1st it shows correct value but after scrolling it get changed every time.
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
//creating a cell using the custom class
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ShowPercentageAttandanceTableViewCell
cell.rollNo.text = String(describing: self.mainArrayRoll[indexPath.row])
var location = Int()
for item in self.rollPercentage {
if item.rollCall == indexPath.row {
location = item.absentCount
cell.percentage.text=String(location)
}
else if cell.percentage.text==("Label")
{
cell.percentage.text=String("0")
}
}
return cell
}
Here is the code.
[![Here in image 2 label is having text =2 and gray color label is having text 0 but it automatically get change after scroll as shown in image 2[![image 2 label changes after scroll but it is showing wrong][2]][2]
You are failing to set the cell.percentage.text to "0" when you didn't find the value you were looking for and you had a reused cell.
Try this:
if let item = self.rollPercentage.first(where: { $0.rollCall == indexPath.row }) {
location = item.absentCount
cell.percentage.text = String(location)
} else {
cell.percentage.text = "0"
}