I've been trying to save the state or order of cells in a UICollectionView after they have changed locations in the UICollectionView. When I use indexPathsForVisibleItems I get indexes that are always in the same order. They do not reflect order change. If I could get the object at each indexPath I could at least make an attempt to iterate through them to get the resulting order.
I used this code. And found that it returned a refreshed state of indexPaths in [0,0]. The ordering is always the same because the it tells me what cells have something in them not what is in each cell. I just don't know enough or where to start to get the object/item in each cell.
NSArray *visible = [self.collectionView indexPathsForVisibleItems];
NSMutableArray *rowsArray = [NSMutableArray arrayWithCapacity:[visible count]];
[visible enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) {
[rowsArray addObject:#(indexPath.item)];
I need the items or objects at each location. How do I iterate through all the cell locations to retrieve the item there. I plan on using the count of an array used to populate the UICollectionView to loop through all the locations and get the object stored there. There is only one section and cells seem to be storing objects like this <Slide: 0x7585200>
The function above returns NSLogs in this format "<NSIndexPath 0x8490390> 2 indexes [0, 0]"
I need to get what is in that location.
I'm not familiar with NSIndexPath or it's formatting. Even just the command/function would help for me to get an object out. Thanks.
(Addendum) I'm think of using something like [self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]]; Not sure what I can store it in.
CollectionView is a collection of UIViews (call UICollectionViewCell). Each cell has an indexPath (section,row) mean the layout position in collectionView content. In the case you has 1 section, section always = 0.
visibleCells mean all cells visible on screen at that time (visible in scroll frame or collectionView frame).
indexPathsForVisibleItems = indexPath array of visibleCells
cellForItemAtIndexPath is a method to get a collectionViewCell at any indexPath you want, often use to get cell when select:
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
NSLog(#"%#",cell);
}
Investigate more from Apple UICollectionView video and example:
https://developer.apple.com/library/ios/#samplecode/CollectionView-Simple/Introduction/Intro.html
https://developer.apple.com/videos/wwdc/2012/
Related
I want to display some cell-specific text, but I can’t extricate the visible cell from the collection view.
Most helpful ones were these:
how to access from UICollectionViewCell the indexPath of the Cell in UICollectionView
detecting when an iOS UICollectionCell is going off screen
A lower collection view (self addSubview) contains possible items. An upper one (self addSubview) contains selections from the lower. The user presses a button (self addSubview again), the lower view scrolls off to oblivion and the upper one expands such that one cell fills the screen. All the array-building and displaying works fine. No *.nibs.
What doesn’t work is figuring out which cell the app is presenting to the user.
TRY #1: First I tried populating the UILabel in parallel with normal ops. Press the button and cellForItemAtIndexPath:
if (cv.tag == UPPER_CV_TAG) {
UICollectionViewLayoutAttributes *itemAttributes = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
NSIndexPath *tempIndexPath;
tempIndexPath = itemAttributes.indexPath;
NSLog(#"cellForItem indexPath %#",indexPath);
NSLog(#"layout indexPath %#",tempIndexPath);
}
responds as you’d expect:
cellForItem indexPath <blah blah> 2 indexes [0, 0]
layout indexPath < > 2 indexes [0, 0]
cellForItem indexPath < > 2 indexes [0, 1]
layout indexPath < > 2 indexes [0, 1]
cellForItem indexPath < > 2 indexes [0, 2]
layout indexPath < > 2 indexes [0, 2]
When the gyrations conclude, Cell 0 correctly occupies center stage. Unfortunately, so does the description for cell 2. Scroll left / right, cells change accordingly, the corresponding text does not. It remains constant: that which maps to cell 2.
Obviously I’m passing the wrong index to the description array.
TRY #2: OK, fine. I’ll interrogate a point I know the currently visible cell occupies and reason backward from that.
CGPoint p = CGPointMake(512, 456);
NSIndexPath *theCellsIndexPath = [self.upper_collectionView indexPathForItemAtPoint:p];
NSLog(#"theCellsIndexPath=%d",theCellsIndexPath.item);
No cigar:
cellForItem indexPath < > 2 indexes [0, 0]
theCellsIndexPath=0
cellForItem indexPath < > 2 indexes [0, 1]
theCellsIndexPath=0
cellForItem indexPath < > 2 indexes [0, 2]
theCellsIndexPath=0
No change from “0” when I change 512,456 to other likely points. No change from “0” when I use layoutAttributes to determine where in the view the cell lies, just in case. Nope, that cell is a really, really big target. I’m not missing it.
TRY #3: Maybe I’m working too hard. I’ll trap the goings-on inside
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
}
and roll with that.
Nope. It never gets called.
TRY #4: Since I don’t know the indexPath of the single cell on the screen (but the collectionView does) I suppose I could iterate through all the cells with didEndDisplayingCell until I hit “Bingo!” That’s crude and indicative of programmer error.
I’m glad the collectionView logic buffers three cells. Probably improves user experience or something. But where it ends up – on 0 – is not where my array index last knew it was – at 2.
What part of the docs didn’t I read?
What part of the docs didn’t I read?
UICollectionView has a method called indexPathsForVisibleItems . Make sure you read it through to see what else you can use from the API.
Try collectionView.visibleCells
NSArray *ary = collectionView.visibleCells;
for (UICollectionViewCell *cell in ary) {
NSIndexPath *path = [collectionView indexPathForCell:cell];
NSLog(#"indexPath of cell: Section: %d , Row: %d", (int)path.section, (int)path.row);
}
I am trying to update a custom tableview cell's progressview but the progress view wont update.
I can set the labels on the tableview cell but accessing things like progress bars or activity indicators doesnt work.
I do the following on the custom cell
cell.progressView.hidden = NO;
cell.progressView.progress = 0.5;
[cell.progressView setNeedsDisplay];
This doesnt work even though in the debugger I see the object is of the correct cell type and the progressView is allocated.
I have tried setNeedsDisplay on the cell itself as well, but no luck. What am I missing?
Calling [self tableView:tableView cellForRowAtIndexPath:indexPath]; is most likely the problem because this likely results in another cell being dequeued from the table. So you end up setting up a new cell, not the one currently being display.
Instead, do this:
CustomTableViewCell *cell = (CustomTableViewCell *)[tableView cellForRowAtIndexPath:indexPath];
This directly asks the table for the cell. This should return the currently displayed cell if the row is visible.
Is it possible to load all cells of an UITableView when the view is loaded so that they are not loaded when I'm scrolling?
(I would show a loading screen while doing this)
Please, it's the only way at my project (sorry too complicate to explain why ^^)
EDIT:
Okay let me explain you, what I'm definite doing:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *cellIdentifier = [NSString stringWithFormat:#"Identifier %i/%i", indexPath.row, indexPath.section];
CustomTableCell *cell = (CustomTableCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
NSDictionary *currentReading;
if (cell == nil)
{
cell = [[[CustomTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier] autorelease];
UILabel *label;
UIView *separator;
if(indexPath.row == 0)
{
// Here I'm creating the title bar of my "table" for each section
}
else
{
int iPr = 1;
do
{
currentReading = [listData objectAtIndex:iPr-1];
iPr++;
} while (![[currentReading valueForKey:#"DeviceNo"] isEqualToString:[devicesArr objectAtIndex:indexPath.section]] ||
[readingresultsArr containsObject:[currentReading valueForKey:#"ReadingResultId"]]);
[readingresultsArr addObject:[currentReading valueForKey:#"ReadingResultId"]];
//
// ...
//
}
}
return cell;
}
My error happens in the do-while-loop:
"listData" is an array with multiple dictionaries in it.
My problem ist that when I’m scrolling my table slowly down, all is fine, but when I’m scrolling quickly to the end of the view and then I’m scrolling to the middle, I get the error that iPr is out of the array’s range. So the problem is, that the last row of the first section has already been added to the "readingresultsArr", but has not been loaded or wants to be loaded again.
That’s the reason why I want to load all cells at once.
You can cause all of the cells to be pre-allocated simply by calling:
[self tableView: self.tableView cellForRowAtIndexPath: indexPath];
for every row in your table. Put the above line in an appropriate for-loop and execute this code in viewDidAppear.
The problem however is that the tableView will not retain all of these cells. It will discard them when they are not needed.
You can get around that problem by adding an NSMutableArray to your UIViewController and then cache all the cells as they are created in cellForRowAtIndexPath. If there are dynamic updates (insertions/deletions) to your table over its lifetime, you will have to update the cache array as well.
put a uitableview on a uiscrollview
for example , you expect the height of the full list uitableview is 1000
then set the uiscrollview contentsize is 320X1000
and set the uitableview height is 1000
then all cell load their content even not visible in screen
In my case it was that I used automaticDimension for cells height and put estimatedRowHeight to small that is why tableview loaded all cells.
Some of the answers here and here suggest using automaticDimension for cells height and put mytable.estimatedRowHeight to a very low value (such as 1).
Starting with iOS 15 this approach seems not to work anymore. Hence, another way to achieve the table to "load" all cells could be by automatically scrolling to the last cell. Depending on the tables height and how many rows it can show some cells are discarded but each cell would be loaded and shown at least once.
mytable.scrollEnabled = YES;
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:cellCount - 1 inSection:0];
[mytable scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
mytable.scrollEnabled = NO;
If you want to scroll up again just scroll to the top as outlined here.
Following the comment that was made by juancazalla, I found that if you have a tableView that is using automaticDimension, loading all the cells at once can be best achieved by setting estimatedRowHeight to a low value (such as 1).
I making an app with a table view and a data source (core data). In this table i group several tasks ordered by date, and i have this segmented control.
I want the table to only load the tasks later or equal than today's date, when the user taps the second segment i want to show all tasks, if he taps the first segment the table must only show the later dates tasks again.
The problem is:
1 - I'm using fetchedResultsController associate with a indexPath to get the managed object.
2 - I use the insertRowsAtIndexPaths:withRowAnimation: and deleteRowsAtIndexPaths:withRowAnimation: methods to make the cells appear and disappear. And this mess with my indexPaths, if i want to go to the detail view of an specific row it is associate with a different indexPath, after delete the rows.
This problem was fixed by a method i did, but i still have other problems of indexPaths and cells, and it seems to me that is gone be me messy to each problem a fix.
There is a simple way to do that?
I tried just to hide the cells instead of delete, it works just fine, but in the place of the hidden cells was a blank space, if there is a way to hide these cells and make the non-hidden cells occupy the blank space i think that will be the simplest way.
Anyone can help me?
set the height of the cell to 0 when it hides, and set the height back to the original value when it appears.
TableViewController.h
#interface TableViewController{
CGFloat cellHeight;
}
TableViewController.m
- (void)cellHeightChange{
//if you need hide the cell then
cellHeight = 0;
cellNeedHide.hidden = YES;
//if you need hide the cell then
cellHeight = 44; // 44 is an example
cellNeedHide.hidden = NO;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
{
switch (section) {
// for example section 0 , row 0 is the cell you wanna hide.
case 0:
switch (row) {
case 0:
return cellHeight;
}
}
}
When the user taps on a segment execute a new fetch request on your managed object to give you an appropriate array (either an array of all dates, or the greater/equal dates). Then use reloadData on the tableView using this new array in the datasource.
or
Give the cell's you wish to hide a height of 0?
how can I reload and animate just one cell/row ?
Right now i download some files. Everytime a file finished downloading, i call it's finished delegate and call [tableview reload].
But then the whole table reloads. And how can i animate the table, so that it doesn't blink in. For example a fade effect or so.
greets Max
Use the following UITableView instance method:
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
You have to specify an NSArray of NSIndexPaths that you want to reload. If you just want to reload. If you only want to reload one cell, then you can supply an NSArray that only holds one NSIndexPath. For example:
NSIndexPath* rowToReload = [NSIndexPath indexPathForRow:3 inSection:0];
NSArray* rowsToReload = [NSArray arrayWithObjects:rowToReload, nil];
[myUITableView reloadRowsAtIndexPaths:rowsToReload withRowAnimation:UITableViewRowAnimationNone];
You can see the UITableViewRowAnimation enum for all the possible ways of animating the row refresh. If you don't want any animation then you can use the value UITableViewRowAnimationNone, as in the example.
Reloading specific rows has a greater advantage than simply getting the animation effect that you'd like. You also get a huge performance boost because only the cells that you really need to be reloaded are have their data refreshed, repositioned and redrawn. Depending on the complexity of your cells, there can be quite an overhead each time you refresh a cell, so narrowing down the amount of refreshes you make is a necessary optimization that you should use wherever possible.
Swift 4 & 5
For TableView
let index = IndexPath(row: 0, section: 0)
tableView.reloadRows(at: [index], with: .automatic)
For CollectionView
let index = IndexPath(item: 2, section: 0)
collectionView.reloadItems(at: [index])
If you only need to update the text in the tableview cell..
UITableViewCell *cell = [_myTableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]];
cell.textLabel.text = [NSString stringWithFormat:#"%i", (int)_targetMaxDistanceSlider.value];
Swift
You can use
let selectedIndexPath = IndexPath(item:0 , section: 0)
self.tableView.reloadRows(at: [selectedIndexPath], with: .none)
in order to reload a specific cell.
Apple Document
done it with new syntax
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
To reload particular cells in a tableView at given indexPaths, you need to create an array of indexPaths and then call function reloadRowsAtIndexPaths on the tableView.
Here is the example:
Swift 5:
let indexPathsToReload = [indexPath1, indexPath2, indexPath3]
tableView.reloadRows(at: indexPathsToReload, with: .none)
Objective C:
NSArray *indexPaths = #[indexPath1, indexPath2, indexPath3];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
or
NSArray *indexPaths = [NSArray arraywithobject:#"indexPath1", #"indexPath2", #"indexPath3",nil];
[self.tableView reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];