UISegmented control being reloaded on UITableView - uitableview

I have a dynamic UITableView. On the first row of the first section I have a segmentedControll (everything with prototype cells, subclassing and delegating to the UITableViewController). With this segmented control I want an additional row to appear on the very same section. What I do is to have a conditional on the numberOfRowsInSection and then when the SegmentedControl is tapped it triggers the following:
if (cellPath.section==0 && cellPath.row==1) {
if (segmentedCell.segmentControl.selectedSegmentIndex==0) {
reverse=NO;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
}else if (segmentedCell.segmentControl.selectedSegmentIndex==1){
reverse=YES;
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
it somehow works, but the problem is that the SegmentedControl also gets reloaded at its initial state and you can inmagine the rest.
Any idea on how to workaround this? I've also tried reloadRowsAtIndex..., but doesn't work (I did put the begin/end ).
Thanks in advance!

When you reload a table the cell are "reused". So each cell will not store state but relies on another class (delegate) to hold the state. So if you wish to maintain the state on your segmented control, then store its value in the delegate for your table view and then ensure you set that value to your segmented control in you cellForRowAtIndexPath each time for its index path.

Related

iOS7: Tableview doesn't load accessory changes

So I got this method that I call on didSelectRowAtIndexPath:
- (void)selectTableViewRow:(int)row {
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:1]] setAccessoryType:UITableViewCellAccessoryNone];
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1]] setAccessoryType:UITableViewCellAccessoryNone];
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:1]] setAccessoryType:UITableViewCellAccessoryNone];
[[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:row inSection:1]] setAccessoryType:UITableViewCellAccessoryCheckmark];
}
(It's only three cells so no for-loop)
It works perfectly fine. When a cell gets selected, there appears a checkmark and the others get removed. But when I call this method in viewDidLoad nothing changes graphicly.
Do I have to reload the table view after setAccessoryType?
This is because your tableView will reload its data after -viewDidLoad is completed. This means that your table view will call -tableView:cellForRowAtIndexPath: in your table view data source, and it will use whatever cell accessory is specified there.
You should specify your cell accessories in tableView:cellForRowAtIndexPath:
At viewDidLoad time, your cells won't get created until they're needed. You have to be careful here, as cellForRowAtIndexPath, if done properly can generate entirely new cells, and throw out ones you already created. You should move your code for setAccessoryType into your cellForRowAnIndexPath, and have it check to see if each cell should be selected based on a variable which is set to the currently selected row and compare it to the indexPath.row.
The issue is that iOS can toss and reuse cells. If you set cell 1's accessory view in your routine, it might get scrolled off the end, and reused elsewhere, say now as cell 7. So cell 7 has the checked accessory view, and cell 1 is off screen.
In other words, cellForRowAtIndexPath feeds cells to the tableView. It's up to you to set their contents correctly based on the indexPath you get called with, especially if you reuse the cell - you have to clear the accessory view or set it for every cell that's called.

Stop [tableView loadData] from deselecting row using Xcode 5 with UIViewController

Here is my program. I want to create a simple list of items that display a number. When the rows are tapped the number will increment by one.
EDIT: Is it proper to change the UI of a row in the didSelectRowAtIndexPath function?
I created a UIViewController in Xcode 5 through a storyboard and it does everything right except I can't seem to stop the [tableView reloadData] from deselecting my row after being tapped. Specifically, I want the row to turn gray and then fade out normally.
I have tried selecting the row and then deselecting the row programatically after calling [tableView reloadData], but it doesn't work.
I know that if I was using UITableViewController that I could just call [self setClearsSelectionOnViewWillAppear:NO], but I'm not.
Is there a similar property I can set for UIViewController?
Here is the code:
[tableView beginUpdates];
[counts replaceObjectAtIndex: row withObject: [NSNumber numberWithInt:newCount]];
[tableView reloadData];
[tableView endUpdates];
I feel I may not be describing what is going on. I have a row that uses UITableViewCellStyle2, which displays a label to the left and right. On the right aligned text is a number that increments each time the row is tapped. Simply updating the data structure does not solve the problem. I need to update it visually. I don't need or want to replace the row, unless I have too. I just want to update the right-aligned text field AND keep the row from being deselected immediately without animation. I can do one or the other, but not both.
Is there a way to just update the right-aligned text field while still staying true to the MVC model?
Remove the [tableView reloadData]; from the code. It should not be called in the methods that insert or delete rows, especially within an animation block implemented with calls to beginUpdates and endUpdates .
Call reloadData method to reload all the data that is used to construct the table, including cells, section headers and footers, index arrays, and so on. For efficiency, the table view redisplays only those rows that are visible. It adjusts offsets if the table shrinks as a result of the reload. The table view's delegate or data source calls this method when it wants the table view to completely reload its data.
[tableView beginUpdates];
[counts replaceObjectAtIndex: row withObject: [NSNumber numberWithInt:newCount]];
[tableView endUpdates];
See the developer.apple section - reloadData
If you want to keep the selection after reload, the easy way is
NSIndexPath *selectedRowIndexPath = [tableView indexPathForSelectedRow];
[tableView reloadData];
[tableView selectRowAtIndexPath:selectedRowIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];

UITableView rows not reloading with UITableViewRowAnimationNone

I have a UITableView and in its -tableView:didSelectRowAtIndexPath: method I want to change the appearance of two cells (the one that has just been selected and the one that was selected before). I do that with this method:
// reload both rows to change appearance
if (![indexPath isEqual:activeIndexPath]) {
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:activeIndexPath, indexPath, nil]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
With the parameter UITableViewRowAnimationAutomatic set it works great: the corresponding rows are updated with a smooth transition. But I want those two rows to update immediately without an animation.
I thought that UITableViewRowAnimationNone would do the trick but if I use that constant for the row animation the cells are not being updated at all

UItableview scrollToRowAtIndexPath not displaying last row correctly

I have a tableview with custom cells with dynamic cell heights depending on the cell content.
My problem is the following, when I ask, programmatically, in the viewDidLoad, to scroll to a given position it works, except for the last row. Sometime the row appears but not fully, and sometimes it even does not appear. In both cases I have to scroll manually to see the row.
Here is the code :
[self.tableView reloadData];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:aRow inSection:aSection];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES ];
Is this a bug of iOS ? any workaround ?
As so far i came to know that,
All the operations used to before the view is shown on the screen are initialized in the viewDidLoad all the UI objects, data objects can be allocated and initialized in this method.
All the operations data modifications, UI modifications made to view need to be done in viewDidAppear. Or even some operations can be done in viewWillAppear.
So for your issue, the UITableView scrolling must be done after the table is loaded on & shown on screen i.e., in viewDidAppear.
Also note that viewDidAppear & viewWillAppear will be called each time view is shown to user, so if you want to scroll the table only for the first instance you can have a flag in your header indicating the instance.
[self.tableView reloadData];
dispatch_async(dispatch_get_main_queue(), ^{
NSIndexPath *rowIndexPath = [NSIndexPath indexPathForRow:3 inSection:0];
[self.tableView scrollToRowAtIndexPath:rowIndexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
});
I don't know exactly why, but I guess this approach works because when we add(???) rows and call [tableView reloadData] tableView has no time to update some internal counters (like row counter) and calling [tableView scrollToRowAtIndexPath ...] has no effect since there is no such row at that time (again, probably correct in case you add rows or set tableView's data for the first time). Calling
dispatch_async(dispatch_get_main_queue(), ^{
...
});
after [tableView reloadData] gives tableView enough time to update row counter and perform scroll to existing row.
Vishy's approach works just because it gives enough time but applicable only if you need to scroll exactly one time when screen is loaded. Moreover it requires ugly flag to check every time viewDid/WillAppear.

Table view cell indexing when using insertRowsAtIndexPaths

Ok, I'm stuck. This is an extension of a previous post of mine. Here is what I am trying to do.
I have an Edit button on a navigation bar that when pressed adds a cell at the beginning of my one section table view. The purpose of this cell if to allow the use to add new data to the table; thus it's editing style is Insert. The remaining cells in the table are configured with an editing style of Delete.
Here is my setediting method:
- (IBAction) setEditing:(BOOL)isEditing animated:(BOOL)isAnimated
{
[super setEditing:isEditing animated:isAnimated];
// We have to pass this to tableView to put it into editing mode.
[self.tableView setEditing:isEditing animated:isAnimated];
// When editing is begun, we are adding and "add..." cell at row 0.
// When editing is complete, we need to remove the "add..." cell at row 0.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
NSArray* path = [NSArray arrayWithObject:indexPath];
// fill paths of insertion rows here
[self.tableView beginUpdates];
if( isEditing )
[self.tableView insertRowsAtIndexPaths:path withRowAnimation:UITableViewRowAnimationBottom];
else
[self.tableView deleteRowsAtIndexPaths:path withRowAnimation:UITableViewRowAnimationBottom];
[self.tableView endUpdates];
// We nee to reload the table so that the existing table items will be properly
// indexed with the addition/removal of the the "add..." cell
[self.tableView reloadData];
}
I am accounting for this extra cell in my code, except I now have two index paths = [0,0] - the new cell and the old original first cell in the table. If I add a call to reload the table view cell in setEditing, the cells are re-indexed, but now my table view is no longer animated.
I want my cake and eat it too. Is there another way to accomplish what I am trying to do and maintain animation?
--John
You can do what you want but you need to keep your data source consistent with the table. In other words, When the table is reloaded, tableView:cellForRowAtIndexPath and the other UITableViewDataSource and UITableViewDelegate methods responsible for building the table should return the same cells depending on editing state that you are adding/removing in setEditing:antimated:.
So, when you insert/delete a cell in setEditing:animated: you need to also make sure your data source reflects the same change. This can be tricky if you are adding a special cell to the beginning of a section but the rest of the data is from an array. One way to do this is while reloading the table, if editing, make row 0 the add cell and use row-1 for your array index for subsequent cells. If you do that you'd also need to add one to tableView:numberOfRowsInSection: to account for the extra cell.
Another way would be to have a section for the add cell and it would have 0 rows when not editing, 1 row otherwise and you return the appropriate cell. This will also require you to configure your table and cell(s) appropriate depending on how you want things to look.

Resources