I have an issue where I have a UITableViewController and when the view appears I do some calculations asynchronously which should result in the updating of specific rows in the table.
Inside the viewWillAppear function I calculate the necessary rows that need to be updated as follows:
- (void)reloadPaths:(NSMutableDictionary *)bookingsDict
{
NSMutableArray* indexArray = [NSMutableArray array];
int i = 0;
for (NSString *dateKey in self.eventsToShow) {
NSMutableDictionary *venues = [bookingsDict objectForKey:dateKey];
if (venues) {
int j = 0;
NSArray *events = [self.eventsToShow objectForKey:dateKey];
for (DCTEvent *event in events) {
NSString *venueIdString = [NSString stringWithFormat:#"%#", event.eventVenueId];
if ([venues objectForKey:venueIdString]) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:i];
[indexArray addObject:indexPath];
}
j++;
}
}
i++;
}
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
}
However I notice that the cellForRowAtIndexPath function is never called after and the cells do not get updated correctly. Any idea what the issue might be?
EDIT: I just noticed that if I scroll out of the view of the cell that is supposed to get updated then it gets updated when I scroll back into view. Is there no way to have it update while in view?
How about wrap it?
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
[tableView endUpdates];
Here are some similar problems I have found.
cellForRowAtIndexPath isn't called after reloadRowsAtIndexPaths
UITableView's reloadRowsAtIndexPaths: (NSArray *) indexPaths failing to cause a reload unless you call it twice?
Hope this helps.
EDIT : I think you are not getting indexPath check section might be constant as you are increasing while each object gets traversed:
- (void)reloadPaths:(NSMutableDictionary *)bookingsDict
{
NSMutableArray* indexArray = [NSMutableArray array];
int i = 0;
for (NSString *dateKey in self.eventsToShow) {
NSMutableDictionary *venues = [bookingsDict objectForKey:dateKey];
if (venues) {
int j = 0;
NSArray *events = [self.eventsToShow objectForKey:dateKey];
for (DCTEvent *event in events) {
NSString *venueIdString = [NSString stringWithFormat:#"%#", event.eventVenueId];
if ([venues objectForKey:venueIdString]) {
//changed here as section might be constant as i think it might be
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:j inSection:0];
[indexArray addObject:indexPath];
}
j++;
}
}
i++;
}
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
}
Try this :
//your code here
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationFade];
[self.tableView endUpdates];
For me pushing my reload code to the main queue solved the problem
DispatchQueue.main.async {
self.tableView.reloadRows(at: [indexPath], with: .automatic)
}
Related
I am adding a filter function to my app, but every time a filter is applied a previously opened cell does not close when I change the filter, is there a way to implement this?
I am currently using:
[self.tableview reloadData];
to reload the table every time the filter is applied
To open and close:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self tableView:tableView canCollapseSection:indexPath.section])
{
if (!indexPath.row)
{
// only first row toggles exapand/collapse
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSInteger section = indexPath.section;
BOOL currentlyExpanded = [expandedSections containsIndex:section];
NSInteger rows;
NSMutableArray *tmpArray = [NSMutableArray array];
if (currentlyExpanded)
{
rows = [self tableView:tableView numberOfRowsInSection:section];
[expandedSections removeIndex:section];
}
else
{
[expandedSections addIndex:section];
rows = [self tableView:tableView numberOfRowsInSection:section];
NSLog(#"Section:%d", rows);
}
for (int i=1; i<rows; i++)
{
NSIndexPath *tmpIndexPath = [NSIndexPath indexPathForRow:i
inSection:section];
NSLog(#"INDEX: %#", tmpIndexPath);
[tmpArray addObject:tmpIndexPath];
}
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (currentlyExpanded)
{
dispatch_async(dispatch_get_main_queue(), ^{
[tableView deleteRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
});
}
else
{
dispatch_async(dispatch_get_main_queue(), ^{
[tableView insertRowsAtIndexPaths:tmpArray
withRowAnimation:UITableViewRowAnimationTop];
});
}
}
}
Before tableview reloadData clear the expandedSections array.
[expandedSections removeAllIndexes];
[self.tableView reloadData];
expandedSections Array maintains the status of the expand section details.Due to that its not collapse the view. So clear the array before reload the table view.
My tableview cell is not getting updated on viewDidAppear :
-(void)viewDidAppear:(BOOL)animated
{
[[ApiAccess getSharedInstance] setDelegate:self];
[[[SocektAccess getSharedInstance]getSocket]setDelegate:self];
[[[SocektAccess getSharedInstance]getSocket] reconnect];
_chatSocket =[[SocektAccess getSharedInstance]getSocket];
if(self.updateWill)
{
NSLog(#"viewDidAppear");
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.updateId inSection:0];
NSLog(#"Update Value: %d",self.updateValue);
NSLog(#"Update ID: %d",self.updateId);
TimelineTableViewCell *cell = (TimelineTableViewCell *)[self.tableData cellForRowAtIndexPath:indexPath];
WallPost *data = self.myObject[indexPath.row];
data.commentCount = self.updateValue;
[self.myObject replaceObjectAtIndex:indexPath.row withObject:data];
WallPost *data2 = self.myObject[indexPath.row];
NSLog(#": %d",data2.commentCount);
cell.commentLabel.text = #" ";
[self.tableData beginUpdates];
[self.tableData reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableData reloadData];
[self.tableData endUpdates];
}
self.updateWill = NO;
}
The self.updateWill is a boolean and has been triggered from another class :
if(self.responseAdd.responseStat.status)
{
self.commentTxt.text=#"";
[self getData];
TimelineViewController *t = (TimelineViewController *)self.navigationController.viewControllers[0];
t.updateWill = YES;
t.updateId = self.index;
t.updateValue =(int)self.response.responseData.count+1;
NSLog(#"response %d",(int)self.response.responseData.count);
}
as you can see i have created a object of my Main class which is TimelineViewController and has added the value of updateWill and other needed properties. But the cell is not getting reloaded!! whats i am doing wrong in here ??
update
(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TimelineTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell" forIndexPath:indexPath];
self.counter = 0;
WallPost *data = self.myObject[indexPath.row];
cell.image.userInteractionEnabled = YES;
cell.image.tag = indexPath.row;
if(self.updateWill == YES)
{
NSLog(#"YES YES");
data.commentCount= (int)self.updateValue;
[self.tableData beginUpdates];
[self.tableData reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableData reloadData];
[self.tableData endUpdates];
cell.commentLabel.text = [NSString stringWithFormat:#"%d comments",data.commentCount];
}
else
{
data.commentCount = (int)data.comments.count;
cell.commentLabel.text = [NSString stringWithFormat:#"%d comments",data.commentCount];
}
This is what you need to workaround in objective c
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.tableData reloadData];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
//Background Thread
dispatch_async(dispatch_get_main_queue(), ^(void){
//Run UI Updates
NSArray *paths = [self.tableData indexPathsForVisibleRows];
for (NSIndexPath *path in paths)
{
//get desired cell here
CustomCell *cell = (CustomCell *)[(UITableView *)self.view cellForRowAtIndexPath: path
];
cell.labeView //now you can update here on your cell items
}
});
});
}
and this is my original logic in Swift
override func viewDidAppear(animated: Bool)
{
super.viewDidAppear(animated)
tableView.reloadData()
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//get visible cells indexpaths
if let indices = self.tableView.indexPathsForVisibleRows
{
for index in indices
{
if index.section == 0 //this is specific to me, checking if section 0 is visible
{
//get desired cell here
let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0,inSection: 0)) as! CustomCell
cell.myImageView.userInteractionEnabled = true //now i can update imageview on table cell
}
}
}
})
}
In viewDidLoad
[self.tableData reloadData];
I hope it will be useful.
I've a UITableView with custom UITableViewCell in a Master-Detail structure. The cells of the table can be documents or folder...if a cell is a document, it opens the document in the detail view, but if is a folder it opens other cells below.
This works perfectly on iOS 7, but running in iOS 8, when I tap a cell, my app freezes and it takes more and more memory...at the end it crashes.
I've tried EVERYTHING...I've searched EVERYWHERE...nothing seems to work!!!
Here is my didSelectRowAtIndexPath: method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"OBJECTS: %lu", (unsigned long)_objects.count);
[tableView deselectRowAtIndexPath:indexPath animated:YES];
NSDictionary *d=[_objects objectAtIndex:indexPath.row];
if([d valueForKey:#"Objects"])
{
NSArray *ar=[d valueForKey:#"Objects"];
BOOL isAlreadyInserted=NO;
for(NSDictionary *dInner in ar )
{
NSInteger index=[_objects indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted)
{
[self miniMizeThisRows:ar];
}
else
{
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar )
{
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[_objects insertObject:dInner atIndex:count++];
}
[self addRowsAtIndexPaths:arCells];
}
}
else
{
NSDictionary *object = [_objects objectAtIndex:indexPath.row];
self.detailViewController.detailItem = [object objectForKey:#"name"];
self.detailViewController.title = [object objectForKey:#"displayedName"];
if(![cache containsObject:object])
{
TableCustomCell *cell = (TableCustomCell*)[tableView cellForRowAtIndexPath:indexPath];
[cell.marker setHidden:NO];
[cache addObject:object];
}
}
}
And in addRowsAtIndexPaths: I just do
- (void)addRowsAtIndexPaths:(NSArray*)indexPaths
{
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationMiddle];
[self.tableView endUpdates];
}
Someone helps???
Thank you.
EDIT
I figured out that the cycle is caused by my UITableViewCell sublcass...
I used this code to manage the indentation:
- (void)layoutSubviews
{
[super layoutSubviews];
float indentPoints = self.indentationLevel * self.indentationWidth;
self.contentView.frame = CGRectMake(indentPoints,
self.contentView.frame.origin.y,
self.contentView.frame.size.width - indentPoints,
self.contentView.frame.size.height);
}
By commenting this, the app works...but the cells aren't indented!
Try to press 'Pause' button at debugger, during this freeze, and look on callStack, than, press 'Continue' button, and again 'Pause', and look for calls, witch of them is the same. It looks like call cycle.
My current tableview expands the cells on click, example:
Parent 0
-Child 1
-Child 2
-Child 3
What I'm struggling to do is, when I expand a cell all the others will close, I'm trying to make sure only one cell Is open at the time. Can you guys give any ideas on how to do it?
Current code for expanding the cell:
- (void)tableView:(UITableView *)tableView1 didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section==0) {
if([d valueForKey:#"produtos"]) {
NSArray *ar=[d valueForKey:#"produtos"];
BOOL isAlreadyInserted=NO;
for(NSDictionary *dInner in ar ){
NSInteger index=[self.firstForTable indexOfObjectIdenticalTo:dInner];
isAlreadyInserted=(index>0 && index!=NSIntegerMax);
if(isAlreadyInserted) break;
}
if(isAlreadyInserted) {
[self miniMizeFirstsRows:ar];
} else {
NSUInteger count=indexPath.row+1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar ) {
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.firstForTable insertObject:dInner atIndex:count++];
}
[tableView1 beginUpdates];
[tableView1 insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationNone];
[tableView1 endUpdates];
[tableView1 scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}else{
NSLog(#"child %# %#|",[item valueForKey:#"nome"],[item valueForKey:#"produtos"]);
}
}
Current code for minimizing the cell:
-(void)miniMizeFirstsRows:(NSArray*)ar{
NSLog(#"miniMizeFirstsRows");
for(NSDictionary *dInner in ar ) {
NSUInteger indexToRemove=[self.firstForTable indexOfObjectIdenticalTo:dInner];
NSArray *arInner=[dInner valueForKey:#"produtos"];
if(arInner && [arInner count]>0){
[self miniMizeFirstsRows:arInner];
}
if([self.firstForTable indexOfObjectIdenticalTo:dInner]!=NSNotFound) {
[self.firstForTable removeObjectIdenticalTo:dInner];
[tableView beginUpdates];
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:
[NSIndexPath indexPathForRow:indexToRemove inSection:0]
]
withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
}
}
Thanks in Advance.
EDIT still Can't make it work
What I have, by using the help from Marco answer:
NSLog(#"indexPath1 = %i",selectedRow);
NSDictionary *d=[self.firstForTable objectAtIndex:indexPath.row];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
NSUInteger count=indexPath.row +1;
NSMutableArray *arCells=[NSMutableArray array];
for(NSDictionary *dInner in ar ) {
[arCells addObject:[NSIndexPath indexPathForRow:count inSection:0]];
[self.firstForTable insertObject:dInner atIndex:count++];
}
[tableView insertRowsAtIndexPaths:arCells withRowAnimation:UITableViewRowAnimationLeft];
// }
}else
{
NSLog(#"Leave Element:::%# %#|",[d valueForKey:#"name"],[d valueForKey:#"book"]);
}
// The user is selecting the cell which is currently expanded
// we want to minimize it back
if (selectedRow == row)
{
NSLog(#"selectedRow2 = %i",selectedRow);
NSDictionary *d=[self.firstForTable objectAtIndex:selectedRow];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
[self miniMizeFirstsRows:ar];
}
selectedRow = -1;
return;
}
// First we check if a cell is already expanded.
// If it is we want to minimize make sure it is reloaded to minimize it back
if (selectedRow >= 0)
{
NSLog(#"selectedRow3 = %i",selectedRow);
NSDictionary *d=[self.firstForTable objectAtIndex:selectedRow];
if([d valueForKey:#"Objects"]) {
NSArray *ar=[d valueForKey:#"Objects"];
[self miniMizeFirstsRows:ar];
}
selectedRow = row;
}
// Finally set the selected index to the new selection and reload it to expan
selectedRow = row;
[tableView beginUpdates]; [tableView endUpdates];
}
Some more help please :)
One thing missing from Marco's answer is the implementation of "heightForRowAtIndexPath" Delegate method.
So these are the steps you need to follow:
Create a variable to record the selected row number
Update that variable when a row is selected (in "didSelectRowAtIndexPath" delegate method) and call tableView's beginUpdates & endUpdates methods.
Return bigger height when indexPath.row == selectedRow, else return normal height.
You need an instance variable to track your selected cell.
NOTE: The code below is not meant to replace your tableview data source and delegate methods, but as an example to track a selected cell:
Add an NSInteger selectedRow instance variable to your view controller.
In viewDidLoad, initialize selectedRow to indicate no cell will be expanded:
- (void)viewDidLoad
{
[super viewDidLoad];
// Set set our selectedRow to -1 to indicate no cell will be expanded
selectedRow = -1;
}
In tableView:didSelectRowAtIndexPath::
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSInteger row = indexPath.row;
// The user is selecting the cell which is currently expanded
// we want to minimize it back
if (selectedRow == row)
{
selectedRow = -1;
[tableView beginUpdates]; [tableView endUpdates];
return;
}
// First we check if a cell is already expanded.
// If it is we want to minimize make sure it is reloaded to minimize it back
if (selectedRow >= 0)
{
selectedRow = row;
}
// Finally set the selected index to the new selection and reload it to expand
selectedRow = row;
[tableView beginUpdates]; [tableView endUpdates];
}
I want to expand/collapse table view sections.I googled and found a code and its working fine.but the problem is the previously opened section is not closed when a new section is opened.Thanks
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gestureRecognizer{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:gestureRecognizer.view.tag];
if (indexPath.row == 0) {
BOOL collapsed = [[arrayForBool objectAtIndex:indexPath.section] boolValue];
collapsed = !collapsed;
[arrayForBool replaceObjectAtIndex:indexPath.section withObject:[NSNumber numberWithBool:collapsed]];
//reload specific section animated
NSRange range = NSMakeRange(indexPath.section, 1);
NSIndexSet *sectionToReload = [NSIndexSet indexSetWithIndexesInRange:range];
[self.aTableView reloadSections:sectionToReload withRowAnimation:UITableViewRowAnimationFade];
}
}
I tried the following code which is working but the animation is not cool as all the sections are reloading.
NSMutableArray *isSectionTouched =[[NSMutableArray alloc]initWithCapacity:arrayForBool.count];
isSectionTouched=[arrayForBool mutableCopy];
for(int i = 1; i <[arrayForBool count] ; i ++){
if(i != gestureRecognizer.view.tag){
[isSectionTouched replaceObjectAtIndex:i withObject:[NSNumber numberWithBool:NO]];
}else{
if ([[isSectionTouched objectAtIndex:gestureRecognizer.view.tag]boolValue]==YES) {
[isSectionTouched replaceObjectAtIndex:gestureRecognizer.view.tag withObject:[NSNumber numberWithBool:NO]];
}else if ([[isSectionTouched objectAtIndex:gestureRecognizer.view.tag]boolValue]==NO){
[isSectionTouched replaceObjectAtIndex:gestureRecognizer.view.tag withObject:[NSNumber numberWithBool:YES]];
}
}
}
arrayForBool=isSectionTouched;
NSRange range = NSMakeRange(1,arrayForBool.count - 1);
NSIndexSet *sectionToReload = [NSIndexSet indexSetWithIndexesInRange:range];
[self.tableView reloadSections:sectionToReload withRowAnimation:UITableViewRowAnimationFade];
Just before you replace the selected index path, iterate over arrayForBool and set each item to NO. You could hold a property to store the currently open section index but unless you have hundreds of sections it isn't worth it.