I have a UITableView with custom UITableViewCell and datasource.
My tableviewdelegate class is registered to callback on on an event and once I got the event, I am reloading the data on the tableview.
The call back comes after I edit the tableviewcell data. But once the callback came, when I reloaddata, the tableview is scrolling to the top; and not staying at the current selection.
So I have cached the current selected cell and trying to scrollToRowAtIndexPath.
But I am hitting a crash.
How can I scroll to that row after tabledata is loaded ?
{
[m_tableView reloadData];
if(m_currentEditingCell != nullptr)
{
[m_tableView scrollToRowAtIndexPath:m_currentEditingCell atScrollPosition:UITableViewScrollPositionNone animated:YES];
}
}
You should provide NSIndexPath instead of providing your tableviewcell as argument for the selector scrollToRowAtIndexPath:atScrollPosition: animated:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[yourTableView scrollToRowAtIndexPath:indexPath
atScrollPosition:UITableViewScrollPositionTop
animated:YES];
The following code is able to select the cell after reload.
{
[m_tableView reloadData]; dispatch_async(dispatch_get_main_queue(), ^{ NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([m_tableView numberOfRowsInSection:([m_tableView numberOfSections]-1)]-1) inSection:([m_tableView numberOfSections]-1)]; if(m_currentCell != nullptr ) [self selectCurrentCell:m_currentCell];
}
}
Related
I am trying to create a chat bot like application, I have used a UITableView with custom cells to fit my needs. Whenever a new message is added, I create and insert a new row and then scroll to the bottom of the UITableView. Everything works fine till a certain point, but when the height of the cells change (I have two different type of cells), the animation is messy, it doesn't smoothly scroll to the end and the entire UITableView flashes, which is not a good user experience. I have tried a couple of approaches:
1 - Add the data to the data source array and reload the UITableView, then scroll to the bottom.
2 - Use insertRowsAtIndexPaths then scroll to the bottom.
Both of them have the same issue with scrolling. I have used scrollToRowAtIndexPath to get to the bottom of the UITableView
I have uploaded the code of a demo app that represents simulate the same issue here so it will be easy to understand. Here is a video of the issue. Any help is really appreciated.
This issue MAY NOT occur on a simulator, kindly run the demo project on a device.
After reading all the comments and having a discussion in chat, I noticed this is happening on the iPhone 5C (10.3.3). I ran the demo on an iPhone 5S (11.3) and the issue does not occur. Not sure if this has to do something with the OS.
Reloading whole tableview results in messy scrolling instead insert row at last position.
Make following changes in your Action Methods
- (IBAction)btnLargeCellClicked:(id)sender { // To add large green colored row.
/************ This is the FIRST approach. ***********/
[arrHeight addObject:#"100"];
[arrData addObject:#"1"];
NSIndexPath* ip = [NSIndexPath indexPathForRow:[_myTable numberOfRowsInSection:0] inSection:0];
[_myTable insertRowsAtIndexPaths:#[ip] withRowAnimation:UITableViewRowAnimationFade];
[self scrollTableviewToLastRow];
/************ This is the SECOND approach. ***********/
// [arrHeight addObject:#"100"];
// [_myTable beginUpdates];
// NSIndexPath *row1 = [NSIndexPath indexPathForRow:arrData.count inSection:0];
// [arrData insertObject:#"1" atIndex:arrData.count];
// [_myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1, nil] withRowAnimation:UITableViewRowAnimationFade];
// [_myTable endUpdates];
// [self scrollTableviewToLastRow];
}
And
- (IBAction)btnClicked:(id)sender { // To add small red colored row.
/************ This is the FIRST approach. ***********/
[arrHeight addObject:#"50"];
[arrData addObject:#"1"];
NSIndexPath* ip = [NSIndexPath indexPathForRow:[_myTable numberOfRowsInSection:0] inSection:0];
[_myTable insertRowsAtIndexPaths:#[ip] withRowAnimation:UITableViewRowAnimationFade];
[self scrollTableviewToLastRow];
/************ This is the SECOND approach. ***********/
// [arrHeight addObject:#"50"];
// [_myTable beginUpdates];
// NSIndexPath *row1 = [NSIndexPath indexPathForRow:arrData.count inSection:0];
// [arrData insertObject:#"1" atIndex:arrData.count];
// [_myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1, nil] withRowAnimation:UITableViewRowAnimationFade];
// [_myTable endUpdates];
// [self scrollTableviewToLastRow];
}
Hope this helps :)
After seeing your source, can say tableView reloadData reloads cells every time, that makes animation more busy every additional cell.
For your goals you should use another system:
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[indexPathOfYourCell] withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tableView endUpdates];
And than, maybe you will need scrollToRowAtIndexPath at the end too
You would need this, change second approach like below
[arrHeight addObject:#"50"];
// [_myTable beginUpdates];
NSIndexPath *row1 = [NSIndexPath indexPathForRow:arrData.count inSection:0];
[arrData insertObject:#"1" atIndex:arrData.count];
[_myTable insertRowsAtIndexPaths:[NSArray arrayWithObjects:row1, nil] withRowAnimation:UITableViewRowAnimationNone];
[_myTable reloadRowsAtIndexPaths:#[row1] withRowAnimation:UITableViewRowAnimationNone];
// [_myTable endUpdates];
[self scrollTableviewToLastRow];
I have a table view containing searchable data, and one cell which contains an editable textField (editable with a pickerView). I am using the pickerView to select the section of the tableview to be displayed. However, when a new row is selected in the pickerView, I don't want the pickerView to be dismissed, which is what happens when I use
[self.tableView reloadData];
I've tried the following to solve the problem, attempting to reload the table except for the cell containing the textField:
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
{
NSIndexSet *toReload = [[NSIndexSet alloc] initWithIndex:1];
if (row == 0)
{
self.selectedDepartmentField.text = #"All";
toReload = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(1, 13)];
}
else
{
self.selectedDepartmentField.text = [self.departments objectAtIndex:(row - 1)];
}
[self.tableView reloadSectionIndexTitles];
[self.tableView beginUpdates];
[self.tableView reloadSections:toReload withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
This crashes with EXC_BAD_ACCESS (code=1,address=0x28)
How should I go about reloading the data in the table to keep the pickerView active?
Following is one possible approach
Get an array of NSIndexPath with
NSArray *visibleIndexPaths = [self.tableView indexPathsForVisibleRows];
Remove the indexpath of the cell with picker from visibleIndexPaths .
Reload the tableview using following method
[self.tableView reloadRowsAtIndexPaths:visibleIndexPaths withRowAnimation:UITableViewRowAnimationNone];
This should reload all visible cells other than one with picker.
I am making a chat application. When the keyboard appears, my UITableView and toolbar (with textfield and button) move up using the setFrame method.
The problem occurs when I am receiving a message from another user while I'm typing a message. When that happens (i.e. after an insertRowsAtIndexPaths to the UITableView), my UITableView and toolbar "reset" to their original positions.
Is this normal behavior caused by the system? I can't find anything in my code that would do this. I want that the toolbar remains visible so I can still type my message.
This is the code to add a convoItem (conversation item) to my array and UITableView:
- (void)insertConvoItem:(ConvoItem *)item
{
[_convoItems addObject:item];
// Update the table view
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:([self.convoItems count] - 1) inSection:0];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
// Scroll to the bottom so we focus on the latest message
NSUInteger numberOfRows = [self.tableView numberOfRowsInSection:0];
if (numberOfRows) {
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(numberOfRows - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
}
It's normal. Receiving new message resets your table view to it's original position.
I am a new IOS Programmer, and i am having a issue.
I have a UITableView with 2 sections, one then is static, and another one is dynamical.
In specific actions i need to add new rows for the second section in runtime..
I know how to manage a UITableView , but not a specific section
Could you help me please?
Best Regards you all
You can use insertRowsAtIndexPaths: method of UITableView
//Update data source with the object that you need to add
[tableDataSource addObject:newObject];
NSInteger row = //specify a row where you need to add new row
NSInteger section = //specify the section where the new row to be added,
//section = 1 here since you need to add row at second section
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:section];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[self.tableView endUpdates];
You can use the same method insertRowAtIndexPath like
// Add the items into your datasource object first. Other wise you will end up with error
// Manage the number of items in section. Then do
NSIndexPath *indexPath1 = [NSIndexPath indexPathForRow:0 inSection:1];
NSIndexPath *indexPath2 = [NSIndexPath indexPathForRow:1 inSection:1];
[self.tableView insertRowsAtIndexPaths:#[indexPath1,indexPath2] withRowAnimation:UITableViewRowAnimationTop];
This will insert two rows to the section1. Remember before doing this you have manage your datasource object.
Swift 3 version
// Adding new item to your data source
dataSource.append(item)
// Appending new item to table view
yourTableView.beginUpdates()
// Creating indexpath for the new item
let indexPath = IndexPath(row: dataSource.count - 1, section: yourSection)
// Inserting new row, automatic will let iOS to use appropriate animation
yourTableView.insertRows(at: [indexPath], with: .automatic)
yourTableView.endUpdates()
you can do this by using scrolltoinfinite method but before that u have to import 3rd party svpulltorefresh
[self.tableViewMixedFeed addInfiniteScrollingWithActionHandler:^{
CGFloat height = self.tableViewMixedFeed.frame.size.height;
CGFloat contentYoffset = self.tableViewMixedFeed.contentOffset.y;
CGFloat distanceFromBottom = self.tableViewMixedFeed.contentSize.height - contentYoffset;
if(distanceFromBottom<contentYoffSet)
{
[weak insertRowAtBottom];
}
}];
-(void)insertRowAtBottom
{
[self.tableViewMixedFeed beginUpdates];
[self.tableViewMixedFeed insertRowsAtIndexPaths:indexPaths1 withRowAnimation:UITableViewRowAnimationTop];
[self.tableViewMixedFeed endUpdates];
[self.tableViewMixedFeed.infiniteScrollingView stopAnimating];
}
here indexpaths1 is the array of indexpaths of next cells which u want to insert in a table .
try using loop to get the next set of arrays and store them into indexpaths1.
[self.tableView insertRowsAtIndexPaths:#[indexPath1,indexPath2] withRowAnimation:UITableViewRowAnimationTop];
USE THIS METHOD..
I am using [tableview reloadData]; to reload the data in my UItableView, however when I use this I loose my highlight on my UItableVewCell.
I would like to know the best way to reinstate this highlight.
I set a tempIndexPath when the user selects the cell they edit the information then I call reloadData, then inside cellForRowAtIndexPath I use this code to re-highlight the cell however its not working.
if ([tempIndexPath isEqual:indexPath]) {
cell.selected = YES;
}
This code keeps the highlighted selection, and is safe with resorting/inserts/repositioning since it keeps a reference to the underlying object from the model, instead of the index path. It also scrolls to the selection, which is helpful when the updated model causes the selection to be moved out of the current scroll position's frame.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Save the selected object at this row for maintaining the highlight later in reloadData
_selectedItem = [_items objectAtIndex:indexPath.row];
}
- (void) reloadData
{
[_itemsTable reloadData];
//Reselect and scroll to selection
int numItems = _items.count;
for (int i = 0; i < numItems; i++) {
NSDictionary *dict = [_numItems objectAtIndex:i];
if (dict == _selectedItem) {
//This is more reliable than calling the indexPathForSelectedRow on the UITableView,
// since the selection is cleared after calling reloadData
NSIndexPath *selectedIndexPath = [NSIndexPath indexPathForRow:i inSection:0];
[_itemsTable scrollToRowAtIndexPath:selectedIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
[_itemsTable selectRowAtIndexPath:selectedIndexPath animated:FALSE scrollPosition:UITableViewScrollPositionNone];
break;
}
}
}
Save the selected row, reload your table view's data and select the row again.
NSIndexPath* selectedIndexPath = [tableView indexPathForSelectedRow];
[tableView reloadData];
[tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
You should know that this is dangerous, because if new items were added to the table view before the selected row, the selection will not be the correct cell. You should calculate how many rows were inserted before the row and adjust the selectedIndexPath accordingly.