how to get rid of the unwanted top cells of table view - ios

I am using custom cell type UITableview.
The custom cell just has two lines of text one below other - key value pair.
The data creating the table is an array sorted based on key.
I need to check whether the top some of the top keys are to be be a part of the table or not.
If not I need to skip the use of those key value pairs and would then use the left array data to create the table. e.g index 0,1,2 may have to be skipped.
When I try to put this condition in cellForRowAtIndexPath: like this
if (key is to be used)
{
cell.key.text = [NSString stringWithFormat:#"%#", [mrow1 etimov]];
cell.value.text = [NSString stringWithFormat:#"%#", [mvalue1 valor]];
}
What this does is that instead of not creating the top 3 cells (say) it creates the cells without actual data of key value but the labels I used in the custom cell for key and value.
What do I change to get rid of the unwanted top cells?

You should change number of rows in tableView:numberOfRowsInSection:. Obviously, if you remove three rows, number of rows will change.
It will also affect the indexPath in tableView:cellForRowAtIndexPath: method. After skipping first three items, indexPath.row = 0 should correspond to third item in item array.
In other words:
numberOfRows = items.count - numberOfSkippedRows
item = items[indexPath.row + numberOfSkippedRows]

Related

Cell Index Path while scrolling does not return intended results

I have a tableview that the user edits information in using textfields, and I store that information into an array that keeps track of all the values. The issue occurs when the user scrolls back to a cell they already edited and the values the added previously are now values from other cells.
I understand that cells are reused, and as a result their data needs to be updated whenever they are being viewed again. I also learned that cellforrowat is called every time a cell is loaded into the view as opposed to just the first time a cell is created. I made a test project to figure out my problem.
My first attempt at solving the problem went like so
cellforrowat is called
if this is the first time the cell is being made set default values and add its data to the array keeping our cell data
If this is not the first time, draw information from the data source at indexpath.row and apply it to the cell
if cellInformation.count < (indexPath.row + 1) // Newly made cell
{
cell.value = 0
cell.tField.text = ""
cellInformation[cellInformation.count] = cell
}
else if (cellInformation.count >= indexPath.row) // Cell we've seen before
{
cell.configure(Value: cellInformation[indexPath.row]!.value) // Sets the textField.text to be the same as the cells value
}
This worked better but when I scrolled back to the top of my tableview, the top most cells were still getting random data. My next attempt generated an ID tag for each cell, and then checking if the id tag of the cell at cellforrowat matched any of the one's in the array.
if cellInformation.count < (indexPath.row + 1) // 0 < 1
{
cell.idTag = idTagCounter
idTagCounter += 1
cell.value = 0
cell.tField.text = ""
cellInformation[cellInformation.count] = cell
}
else if (cellInformation.count >= indexPath.row)
{
for i in 0...idTagCounter - 1
{
if(cell.idTag == cellInformation[i]?.idTag)
{
cell.configure(Value: cellInformation[i]!.value)
}
}
cell.configure(Value: cellInformation[indexPath.row]!.value)
}
This got pretty much the same results as before. When I debugged my program, I realized that when i scroll down my tableview for the first time, indexPath.row jumps from a value like 7 down to 2 and as I scroll more and more, the row goes further away from what it should be for that cell until it eventually stops at 0 even if there are more cells i can scroll to. Once the row hits 0, cellforrowat stops being called.
Any ideas on how i can accurately assign a cells values to the information in my array?
Your premise is wrong:
cellforrowat is called
if this is the first time the cell is being made set default values and add its data to the array keeping our cell data
If this is not the first time, draw information from the data source at indexpath.row and apply it to the cell
You should set up a model object that contains the data for the entries in your table view, and your cellForRowAt() method should fetch the entry for the requested IndexPath.
Your model can be as simple as an array of structs, with one struct for each entry in your table. If you use a sectioned table view you might want an array of arrays (with the outer array containing sections, and the inner arrays containing the entries for each section.)
You should not be building your model (array) in calls to cellForRowAt().
You also should not, not NOT be storing cells into your model. You should store the data that you display in your cells (text strings, images, etc. Whatever is appropriate for your table view.)
Assume that cellForRowAt() can request cells in any order, and ask for the same cells more than once.
Say we want to display an array of animals, and a numeric age:
struct Animal {
let species: String
let age: Int
}
//Create an array to hold our model data, and populate it with sample data
var animals: [Animal] = [
Animal(species: "Lion", age: 3),
Animal(species: "Tiger", age: 7),
Animal(species: "Bear", age: 4)
]
//...
func cellForRow(at indexPath: IndexPath) -> UITableViewCell? {
let cell = dequeueReusableCell(withIdentifier: "cell" for: indexPath)
let thisAnimal = animals[indexPath.row]
cell.textLabel.text = "\(thisAnimal.species). Age = \(thisAnimal.species)"
}
Note that for modern code (iOS >=14), you should really be using UIListContentConfigurations to configure and build your cells.

Swift combine different type into one array to display data for UITableView

I have tableview where I need to show few sections. You should imagine this table like a playlist of your songs. In the top first section I need to display a cell with button which will add more songs to the playlist and other sections of tableview are header titles of Music category (like pop, rock and etc). Each of these sections contains cells which are songs names.
I have an array of songs called like this: var songsGroups = [SongGroup]. Which is actually my datasource.
SongGroup contains few properties:
var categoryName: String
var songs: [Songs]
But the problem appears on the next level. I every time need to check indexPath.section and do like this:
if indexPath.section == 0 {
// this is a section for ADD NEW SONG BUTTON cell no need in header title as there is no data repression only static text on the cell.
} else {
var musicCategoryName = songsGroups[indexPath.seciton - 1]. categoryName
headerTitle.title = musicCategoryName
}
As you see my code became magical by adding this cool -1 magical number. Which I replay don't love at all.
As an idea for sure I can try to combine my ADD NEW SONG BUTTON section (by adding some additional object) with songsGroups array and create NSArray for this purposes. Like in Objective-C as you remember. So then my datasource array will looks like this:
some NSArray = ["empty data for first cell", songsGroups[0], songsGroups[1]... etc]
So then there is no need to check any sections we can trust our array to build everything and even if I will add more empty data cells there is no need for me to handle my code via if block and adding tons of magical numbers.
But the issue I see here that we don't use explicit types of array and it's upset.
So maybe you know more beautiful solutions how to resolve my issue.
You can introduce a helper enum:
enum Section {
case empty
case songCategory(categoryName: String, songs: [String])
}
Your data source would then look something like this:
let datasource: [Section] = [.empty, .songCategory(categoryName: "Category1", songs: ["Song 1", "Song2"])]
So now you can use pattern matching to fill the table view:
let section = datasource[indexPath.section]
if case let .songCategory(categoryName, songs) = section {
headerTitle.title = categoryName
} else {
// this is a section for ADD NEW SONG BUTTON cell no need in header title as there is no data repression only static text on the cell.
}
I am not sure if I understand you right. But is seems to me that you want to display
1) something that lets the user add a new song by tapping a button, and
2) a table of songs, sectioned into groups.
If this is the case, why don’t you put the add new song button in the table header view, and all your song groups and songs in a 2-dim array used as your dataSource?

How to use a label on a tableview cell only IF an indexPath object is available

I have a table view with two labels in each cell. One shows an object from an array and the other shows the corresponding object from another array created previously.
presentArray: [item1, item2, item3]
previousArray: [item1, item2, item3, item4]
numberOfRowsInSection returns presentArray.count
In cellForRowAt I need to set one label to display the corresponding index of the previousArray. This is where the crash occurs. The arrays can be different lengths by their nature.
So, how can I tell the tableview cell to display a string from the previous array only IF it exists.
I can presently only display the string in the label if the arrays are the same length, else I get a crash because the indexPath goes out of range, either longer or shorter.
How can I say cell.label.text = "array[indexPath.row]" IF the index path exists.
Right before you set the text of the label, you could use an if condition like so:
if indexPath.row < previousArray.count {
cell.label.text = array[indexPath.row]
} else {
cell.label.text = ""
}
This code checks to make sure that the indexPath.row of the tableview is less than the number of items in the array, so your code won't crash.

Mix-Matching Collection View Cells within a 1-Section Collection View

I have two different cells: A & B. (where the only difference between A & B is that they have 2 different properties.
I'm trying to get A and B to show up vertically in a collection view like this:
A B A B A B A B
How do I go about doing this in the cellForItemAt method? Maybe by keeping track of which cell was returned last?
There are a few approaches you can take to achieve something like this. If all of your data is the same but you want every other cell to appear differently you can just check to see if it is an even or an odd row when in your datasource methods.
Something like this:
if indexPath.row % 2 != 1 // odd row
{
// set up a cell of type A
}
else // even row
{
// set up a cell of type B
}
Otherwise, if your actual data source can discern the different elements in the array as being of different types, you would just check to see what type of element is in your array at a given index and return the appropriate cell.

Showing/Hiding Rows in Sections of UITableView

I am trying to create a UITableView that is set up so that when I click on a section, it hides the rows from the previous section and shows the rows of the newly selected section. The tableView is set up with data from an array of arrays called menuItems. menuItems contains several other arrays including contactInfoArray, clientInfoArray, jacketArray, and shirtArray. In each of these arrays, the objectAtIndex:0 is the Section Title and effectively acts as the header.
So, for example when the app loads, it should first show all rows of the contactInfoArray, but then show the ObjectAtIndex:0 (the title) for each of the other arrays. When I then tap one of those section titles, for example: "Jacket", I need the table to hide the contactInfoArray objectsAtIndexes:1+, but not object 0, while at the same time (or with an acceptable delay) showing jacketArray objectsAtIndexes:1+.
I have achieved the desired result by calling reload data, but I want animation of the change and reloadData does not allow that. I can't find any tutorials or sample code that does this.
Can anybody help me?
Thanks to my good friend Darren for helping me solve this issue!
- (NSInteger)tableView:(UITableView *)aTableView numberOfRowsInSection:(NSInteger)section{
return (indexPath.section == currentSection) ? [[menuItems objectAtIndex: indexPath.section ] count] : 1;
}
All that I needed to do was set an immediate variable (in my case "currentSection" which is an int) and set its value in the didSelectRowAtIndexPath: method. Then I set up my table so that row 0 of the section acted as the section title.
Doing this made it so that when I clicked on the section header it would "collapse" the open section and "expand" the newly selected section.

Resources