Then I get json update I reload my tableView, but I have tableView with collectionView inside it and I have little lag in visible cell at the moment of reload. I want to reload only invisible cell of tableView. But there is no 'visibleSections' method like 'visibleCells'.
I get all visible sections
NSArray *visibleSections = [[self.tableView indexPathsForVisibleRows]valueForKey:#"section"];
Not I want get all sections, then remove all visibleSections and reload this new array. But I can not find the way to get all sections. How can I do it?
You could use one of these methods:
- (void)reloadRowsAtIndexPaths:withRowAnimation:
- (void)reloadSections:withRowAnimation:
Edit
When you try to get the indexPaths of all invisible cells, you could try something like this:
NSMutableArray *invisibleCells = [NSMutableArray array];
for (int i = 0; i < [tabsTableView numberOfSections]; i++) {
for (int n = 0; n < [tabsTableView numberOfRowsInSection:i]; n++) {
for (NSIndexPath *indexPath in [tabsTableView indexPathsForVisibleRows]) {
if ([indexPath section] == i && [indexPath row] == n) {
goto nextIteration;
}
}
[invisibleCells addObject:[NSIndexPath indexPathForRow:n inSection:i]];
nextIteration:;
}
}
More important is why you would like to do that?! Due UITableViewCells get reused, they won't be updatable. When the cell is "invisible", it changes its content and is used at a different position within the visible cells. This is how UITableView works.
You will (also currently) use this method to set the content of the cell:
- (void)tableView:tableView cellForRowAtIndexPath:indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:_cellID];
if (!cell) {
cell = (UITableViewCell *)[[[NSBundle mainBundle] loadNibNamed:#"cellTemplate" owner:nil options:nil] objectAtIndex:0];
}
// configure cell content here
}
When the cell get's reused, this method gets called and you create the cell only if UITableView does not reuse an invisible cell. If it does, you change the content.
So there is no need to reload invisible cells and it's impossible. From the docs:
Return Value
An object representing a cell of the table or nil if the cell is not visible or indexPath is out of range.
Related
We can get a cell from:
UITableViewCell * cell = [self tableView:tableVie cellForRowAtIndexPath:indexPath]
but it will calling the method cellForRowAtIndexPath:
if I know the cell's tag or indexPath , it's there a way to get the specified cell without to calling cellForRowAtIndexPath:?
Add some reason why I can't call cellForRowAtIndexPath:indexPath
I want to get the cell in the method cellForHeightAtIndexPath to reset the height of each cell. :
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [self tableView:tableView cellForRowAtIndexPath:indexPath];
CommentsListResponseDataModel * model = self.liveCommentData[indexPath.row -1];
if (model.viewHeight > 0) {
return model.viewHeight;
} else {
if (model.parentComment) {
CommentParentCell * cc = (CommentParentCell *)cell;
return cc.viewHeight;
} else {
CommentSingleCell * cc = (CommentSingleCell *)cell;
return cc.viewHeight;
}
}
}
}
But this will become a dead loop because it all cellForRowAtIndexPath:indexPath and cellForRowAtIndexPath:indexPath will recall cellForHeightAtIndexPath:indexPath
As of my understanding, you are trying to get the cell object by calling UITableView datasource method. But when you try to do like that then it will be infinite loop call and you will never get the cell object. So in order to get cell object from UITableView, do the following steps.
Declare your UITableView as global to the your ViewController, something like UITableView *_tableView; or #IBOutlet weak UITableView *_tableView;
Then try to call the
//specify the item indexpath
NSIndexPath *_indexPath = [NSIndexPath indexPathForItem:3 inSection:1];
//now try to access cell object by passing cell indexPath
UITableViewCell *_cell = [_tableView cellForRowAtIndexPath:_indexPath];
It seems you want something unrelated to your original question - you want your table view cells to size automatically, depending on content.
What you need to do is:
1. add constraints to your cell - make sure it doesn't have a height constraint, but its content must have Top and Bottom constraints.
2. remove heightForRowAtIndexPath as it will not be necessary
3. set your table view row height to automatic
tableView.rowHeight = UITableViewAutomaticDimension
Now just setup your cell - resize its content to desired height and table cell will automatically be resized
In cellForRowAtIndexPath: delegate n number of cells (corresponding to indexpath.row) are created and are added to an array cellsArray how to reuse each cell in the array, when cellsArray count is 7? (want to reuse the cells as a batch of 7)
FINameCell *cell = nil;
if ([FINameCellcells count] > indexPath.row)
{
cell = [FINameCellcells objectAtIndex:indexPath.row];
} else {
cell = [[[NSBundle mainBundle] loadNibNamed:#"FINameCell" owner:self options:nil] objectAtIndex:0];
[cellsArray addObject:cell];
}
You're not supposed to manually load cells from NIBs these days. The way to do that is to call UITableView's dequeueReusableCellWithReuseIdentifier:forIndexPath with the cell being either already defined as prototype cell in IB or by associating the cell via registerNib:forCellReuseIdentifier beforehand.
After that, don't worry about cell reuse, it's going to be all automatic.
I created a TableView with Expandable cell as shown here. In my case the expanded area contains a TextView. My view contains a tag (as label text) and its' meaning (in text view). And things are working fine.
I have come up with a situation where a single tag can have multiple meanings. So I need to show all the meanings. So I got to have that many textview's as many meanings for each tag.
I have added a TableView (with TextView in TableCell) inside the expandable part of the table cell. So can add X number of rows in the expanded table cell with each meaning of the tag in single cell. I want to set the height of TextView, TableView and finally TableViewCell. I tried different ways, but am not able to achieve it. Can you help me achieve it the way I am looking for.
Screen shot of something that I am trying to achieve -
UPDATE :-
My cellForRowAtIndexPath method where I am trying to set properties :-
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Table View TAG = %d ROW = %d", tableView.tag, indexPath.row);
// TABLE VIEW IN THE CELL
if (tableView.tag == 10) { // cell.valuesTv
CannedValueCell *ccell = (CannedValueCell *) [tableView dequeueReusableCellWithIdentifier:#"cannedValueCell"];
if (ccell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CannedValueCell" owner:self options:nil];
ccell = [nib objectAtIndex:0];
}
// Populate valuesTv
ccell.valueTextView.text = [valuesArray objectAtIndex:indexPath.row];
UITapGestureRecognizer *gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(valueTextViewTapped)];
[ccell.valueTextView addGestureRecognizer:gestureRecognizer];
//[ccell.valueTextView sizeToFit]; // Makes no difference
// CGRect frame = ccell.valueTextView.frame;
// frame.size.height = ccell.valueTextView.contentSize.height;
// ccell.valueTextView.frame = frame;
NSLog(#"*** TEXTView HEIGHT = %f C_CELL HEIGHT = %f ** TV HEIGHT = %f", ccell.valueTextView.frame.size.height, ccell.frame.size.height, cell.valuesTv.frame.size.height );
// *** TEXTView HEIGHT = 79.000000 C_CELL HEIGHT = 80.000000 ** TV HEIGHT = 80.000000
// AS SET HEIGHT IN STORY BOARD - NO DIFFERENCE
return ccell;
} else {
// MAIN TABLE VIEW
static NSString *cellIdentifier = #"CannedCell";
cell = (CannedCell *) [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CannedCell" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
// Set datasource and delegate for the cell's TableView
cell.valuesTv.dataSource = self;
cell.valuesTv.delegate = self;
.......
}
}
Table view delegate method
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.cannedTV) {
if (selectedIndex == indexPath.row)
return 150; //(30 + (80 * valuesArray.count));
else
return 30;
}
return 30;
}
}
This is what the result is :-
I tried various things, searched a lot on net, but couldn't find solution for the following :-
Set height of Text View as much as the contents of it.
The inner table should be completely visible - in all cells full textview should be visible with its complete contents properly. So each cell may have different height.
Thus the outer/main table expandable cell should also be accordingly set. - For this I tried (30 + (80 * valuesArray.count));, but the inner table doesn't occupy that much space.
How to get which textview is tapped ? When textview is tapped, the method valueTextViewTapped is fired, but yet am not able to find which textview is tapped on. How can I pass the indexPath.row to valueTextViewTapped method, so can find which textview was tapped on ??
Can you please guide me set the height of the components accordingly and achieve the requirements. Any help is highly appreciated. Thanks.
A table view and table view controller are very powerful tools.
Based on the information provided, your suggested implementation is satisfactory.
A UITableViewCell can have its height set dynamically using the table view delegate method heightForRowAtIndexPath.
You can respond to a user selecting the UITableViewCell using the table view delegate method didSelectRowAtIndexPath.
I have been wondering why my code works well with cellForItemAtIndexPath: & not with dequeueReusableCellWithReuseIdentifier: while fetching collection view cells.
Here is my code:
This one works Fine:
NSInteger numberOfCells = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < numberOfCells; i++) {
myCustomCollectionCell *cell = (myCustomCollectionCell *)[self.collectionView cellForItemAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
//here I use the cell..
}
While this compiles well, but not working (the changes I perform on cell is not depicted)
NSInteger numberOfCells = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < numberOfCells; i++) {
myCustomCollectionCell *cell = [self.collectionView dequeueReusableCellWithReuseIdentifier:#"myCell"forIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
//here I use the cell..
}
Tried this too, but no use:
NSInteger numberOfCells = [self.collectionView numberOfItemsInSection:0];
for (NSInteger i = 0; i < numberOfCells; i++) {
myCustomCollectionCell *cell = (myCustomCollectionCell *)[self.collectionView dequeueReusableCellWithReuseIdentifier:#"myCell"forIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
//here I use the cell..
}
Any ideas?
These two are basically two very different methods.
dequeReusableCellWithReuseIdentifier:
Suppose that you have a list of articles to view. Say you have 50 articles. The screen won't show you all 50 articles on screen at once. It will show you limited cells at once, based on the height you given to the rows. Lets say the screen shows only 5 articles at once and now you are at the top of the list. The list will show items 1-5. Now when you scroll, inorder to display the 6th item, the list reuses your 1st cell, configures it for the 6th one and display it. By this time your 1st cell is out of the view.
2.cellForRowAtIndexPath :
On the other hand cellForRowAtIndexPath returns you the cell which is already in the view or from IndexPath you provide. In this case if the cell is already in the memory, it will just return that or it will configure a new cell and return.
The example below is for UITableViews, but UICollectionViews can be handled in the same way.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
/*
* This is an important bit, it asks the table view if it has any available cells
* already created which it is not using (if they are offscreen), so that it can
* reuse them (saving the time of alloc/init/load from xib a new cell ).
* The identifier is there to differentiate between different types of cells
* (you can display different types of cells in the same table view)
*/
UITableViewCell *cell = [tableView dequeueReusableCellWithReuseIdentifier:#"MyIdentifier"];
/*
* If the cell is nil it means no cell was available for reuse and that we should
* create a new one.
*/
if (cell == nil) {
/*
* Actually create a new cell (with an identifier so that it can be dequeued).
*/
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"MyIdentifier"] autorelease];
}
/*
* Now that we have a cell we can configure it to display the data corresponding to
* this row/section
*/
//Configure the cell here..
/* Now that the cell is configured we return it to the table view so that it can display it */
return cell;
}
Let me know if you were still unclear.
They are different things:
cellForItemAtIndexPath gets an already-populated cell from the collection view.
dequeueReusableCellWithReuseIdentifier will possibly return a cell that can be re-used, when populating a new cell. It's designed to help reduce the number of cell objects in existence. If it fails (and it often will) then a new cell must be created explicitly.
I have a UITextView populated with data from a UITableView (tvServices); when the user taps on a row in the UITableView, I move the contents of that cell to the UITextView, after which the contents of the UITextView are stored in a CoreData store.
When the user selects a record for updating, I move the contents of the stored UITextView back into the UITextView. When the user taps on the UITextView, I display the entire UITableView as a UIPopover, with the contents of the UITextView marked with a checkmark (AccessoryCheck) in the UITableView.
Unfortunately, this is not working the way I designed it... nothing gets checked. Here is my code ( this is a proof of concept - where I check every row in the UITableView to make sure it can be done). globalServicesArray is the data source for the UITableView cells:
for (int i = 0; i < sharedServicesArray.globalServicesArray.count; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
UITableViewCell *cell = [tvServices cellForRowAtIndexPath:path];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
I hope this makes sense; if there is a better way of doing this, I'm open for suggestions... otherwise what am I doing wrong?
Assuming only one cell can be edited, can you try this. Store the index path in a class level iVar/Property, say. selectedIndex and reload the cell
//To hold the index paths
NSMutableArray *reloadArray = [NSMutableArray array];
if (self.selectedIndex)
{
//if already selected deselect
[reloadArray addObject:self.selectedIndex];
}
for (int i = 0; i < sharedServicesArray.globalServicesArray.count; i++) {
NSIndexPath *path = [NSIndexPath indexPathForRow:i inSection:0];
[reloadArray addObject:path];
}
[tvServices reloadRowsAtIndexPaths:reloadArray withRowAnimation:UITableViewRowAnimationNone];
In cell for row at index path check for the index path and set accessory checked if matches the index
if (self.selectedIndex &&
self.selectedIndex.row == indexPath.row &&
self.selectedIndex.section == indexPath.section)
{
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}