My current set up is the following:
Root Tab Bar:
Collection view with magazines
Bookmarks (with a table view)
Others
You can add a bookmark from a magazine in the collection view and also remove it from there.
The behaviour I'm seeing is the following:
I start the application, the table view queries the number of sections, number of cells, but not the cellForRowAtIndexPath. I could understand why, as there is no cell in the active view, so no data should be loaded.
When I add a bookmark from the collection view, it adds it to the array (via a notification) and requests the tableview to be reloaded. As there isn't an initial entry, it goes through the motions described above. When I press it again to remove the bookmark the entry is removed from the array. This is where it gets interesting because the first thing the table calls is not the number of sections or rows but the cellForRowAtIndexPath. As the array is empty, the application crashes on a request for data on index 0.
My question is why does the cell creation get called in that order? Is there any way to avoid it?
If you changed the section, try calling - (void)reloadSections:(NSIndexSet *)sections before you call reloadData
https://developer.apple.com/library/ios/DOCUMENTATION/UIKit/Reference/UICollectionView_class/index.html#//apple_ref/occ/instm/UICollectionView/reloadSections:
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationLeft];
The reason this was happening is because I was attempting to change something about the table before I reloaded the data.
[self.tableView setSeparatorStyle: !_helpText.hidden ? UITableViewCellSeparatorStyleNone : UITableViewCellSeparatorStyleSingleLine];
[self.tableView reloadData];
That was for removing the lines so a message can be displayed. However that update was using old data as reloadData had not been called.
[self.tableView reloadData];
[self.tableView setSeparatorStyle: !_helpText.hidden ? UITableViewCellSeparatorStyleNone : UITableViewCellSeparatorStyleSingleLine];
Reversing them fixed the issue.
Related
So I have a UITableView with many sections, each section contains items with a certain letter of the alphabet(Alphabetically sorted). The user has the ability to add more data to this table, so when they add a new item that goes in a section that is not there, a new section must be created. Same problem when deleting, if the last item in a section is deleted the whole section would be removed. How would I animate this?
I have found something similar to this: UITableview reloaddata with animation
But that does not help me. It recommends using this method(In swift):
[_tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
This works great if you have the same number of sections before and after the reload, but mine does not. The number of sections could change causing a crash.
For the purpose you mentioned above, you should not reload data. For adding the new section you should use,
- (void)insertSections:(NSIndexSet *)sections
withRowAnimation:(UITableViewRowAnimation)animation
For deleting the section you should use,
- (void)deleteSections:(NSIndexSet *)sections
withRowAnimation:(UITableViewRowAnimation)animation
I have found 2 functions to reload my rows of UITableView
1st.
[tableView reloadRowsAtIndexPaths:[tableView indexPathsForVisibleRows]
withRowAnimation:UITableViewRowAnimationNone];
2nd.
[tableView reloadData];
What is more specific way to reload UITableView Data
Both are "reliable" and possibly "specific" (depending on what you mean by specific) -- in fact, there are even more ways:
reloadData
reloadRowsAtIndexPaths:withRowAnimation:
reloadSections:withRowAnimation:
reloadSectionIndexTitles
Check the developer documentation (https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITableView_Class/index.html) on the differences, but the short answer is that they are all reliable -- it just depends on how much information you want to reload, and if you want to do it with animation.
For example, if you only want to reload certain rows, you can use reloadRowsAtIndexPaths:withRowAnimation:. If you want to reload a whole section, use reloadSections:withRowAnimation:.
This answer may have two cases,
a) You've the table with at least a single section (with a title or returning a view for section)
In this case, if you call reloadData then it'll also reload section too. But reloadRowsAtIndexPaths:withRowAnimation: will only reload the visible cells and not the section.
b) You've the table with at least a single section (without a title or returning a view for section)
In this case, if you call reloadData or reloadRowsAtIndexPaths:withRowAnimation: both will reload only the visible cells.
So which one is the better?
As I explained, based on the case, you should call any of the one method. If you have sections which you don't want to reload then you should call reloadRowsAtIndexPaths:withRowAnimation: method. Or if you want to reload cells along with section then should call reloadData method.
I have several cells that are written onto a UICollectionView and reloading them takes a long time. How can I reload only when the data of a cell is modified? Here's one thing I've tried:
if([oldDataSource count] != [currentDataSource count])
{
[collectionView reloadData];
}
Yet, I still don't want to deal with reloading the entire thing. I've also tried:
if([oldDataSource count] < [currentdataSource count])
{
//something was added. //reload the last item in the current data source
}
This also failed because I can't reload something that doesn't "exist" in memory yet. This method also doesn't work because if something was deleted from my data source, the Cell will still remain in the interface, and will crash if clicked on.
So, what's the best way to deal with edited cells?
Thanks!
Reloads just the items at the specified index paths.
- (void)reloadItemsAtIndexPaths:(NSArray *)indexPaths
Discussion
Call this method to selectively reload only the specified items. This causes the collection view to discard any cells associated with those items and redisplay them.
try and get the cell u want to change. then:-
If u can get it. change the data source and reload it.
if it doesnt reflect the changes. change the data source, then the repective new values of the cell, then call setNeedsDisplay on the cell.
If the cell is not returned (that means) it is out of the view. Simply change the data source. The change in the data source will be reflected when the cell is brought to view
try any of these three.
cheers. have fun
I have a problem that is driving me crazy.
I have an UITableView that is always in editing mode (it has to be).
The user can add new rows to it.
The navigationItem.leftbarButton of the tableViewController pushes a new controller just to do it, let's call it "newRowsVC".
Before the push the tableViewController set itself as the delegate of the newRowsVC, the protocol has only a method:
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
}
I start adding rows and everything works fine, each now row is immediately displayed in the tableViewController until the last new row will force the tableView to scroll because there won't be anymore screen real estate for it. Here, I have no idea why, the tableView, only it, is as frozen, with no scrollbar and doesn't respond to input. The app continues to run without a crash and I can even dismiss the tableViewController by tapping the navigationItem.rightbarButtonItem.
I can keep creating new rows, they are added to the array, the number of row in the tableView data source is computed correctly. But the table is like dead.
If I dismiss the tableViewController and then I come back to it, I see that all the rows previously created, also the ones not shown as soon as they were created are there!
I really do not have idea of how I can fix this.
The first thing I tried was to force the scroll after the reload of the table but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled:YES];
}
I also tried forcing the tableView to scroll to the last row but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled: YES];
[self.tableView scrollToRowAtIndexPosition: [self.tableView indexPathsForVisibleRows]last object] atScrollPosition: UITableViewScrollPositionBottom animated:YES];
}
I check the number of rows each time the table is reloaded. No error, the number is perfect and the new rows data are correctly in the array, also the data for the cells not shown.
I thought it could be because the tableView is always in editing mode so I tried setting it to NO but nothing changes, id din't fix the problem.
Two notes:
1)the tableView has to be the delegate of each one of it's custom cells. They have an UITextField and an UIStepper, handled by the tableViewController. I already tried to not set the tableViewController as the delegate of its custom cells but nothing changes so the problem is not this.
2) self.tableView.bounces = NO but this has nothing to do with the scrolling issue, the scroll is enabled.
Update: After more tests I found that if I remove the dequeue of the reusable custom cell everything works fine, so the problem should be about the reuse.
static NSString *CellIdentifier=#"MyCell"
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
Nicola
After hours I got it. As pointed out by Amit the problem was hidden in cellForRowAtIndexPath... It was hard to catch because it didn't happened all the times and the first times I enabled/disabled the cell reusing everything seemed the same so I did not link the problem to it. I got back on it after I tried about all the other options I had been able to think about.
The problem was in the reuse of the cells and the fact that the custom cell has the tableView as its delegate to handle its textView and the stepper without exposing them.
I got rid of all the delagion stuff and exposed the textView and the stepper as public properties of the view. By doing this I was able to set the tableViewController directly to be the delegate of the cell.textView and to add directly a target/action for the stepper.
Everything works flawslessly now.
Thanks to everyone who tried to help me!
Nicola
I am using a UITableView to display the results of a series of calculations. When the user hits 'calculate', I add the latest result to the screen. When I add a new cell, the UITableViewCell object is added to an array (which is indexed by tableView:cellForRowAtIndexPath:), and then I use the following code to add this new row to what is displayed on the screen:
[thisView beginUpdates];
[thisView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation: UITableViewRowAnimationFade];
[thisView endUpdates];
This results in the new cell being displayed. However, I then want to immediately scroll the screen down so that the new cell is the lowermost cell on-screen. I use the following code:
[thisView scrollToRowAtIndexPath:newIndexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
This almost works great. However, the first time a cell is added and scrolled to, it appears onscreen only briefly before vanishing. The view scrolls down to the correct place, but the cell is not there. Scrolling the view by hand until this invisible new cell's position is offscreen, then back again, causes the cell to appear - after which it behaves normally. This only happens the first time a cell is added; subsequent cells don't have this problem. It also happens regardless of the combination of scrollToRowAtIndexPath and insertRowsAtIndexPath animation settings.
EDIT:
I've now started inserting cells at the second-to-last position of the table, rather than the end, and the problem still occurs - when first inserted, a cell is 'invisible' until it goes offscreen and comes back on again. What could be causing this, and how can I force the cell to be drawn as soon as it is added to the table?
You're having problems because your updating the table without updating the data model backing it. Tables don't actually know how many rows they have nor what cells to display. They depend on the datasource and the delegate to tell them these things. Your design expects the table itself to track them.
insertRowsAtIndexPaths: is intended to be used for moving existing rows around a table, not for adding entirely new logical rows. When you insert an entirely new cell, the tableview looses track of how many rows it actually has.
Before you display a new row, the first thing you should do is update the values returned by:
– numberOfSectionsInTableView:
– tableView:numberOfRowsInSection:
... to reflect the addition of the new rows. This will allow the table to understand how big it is.
Then you need to update cellForRowAtIndexPath: to return the correct cell for the added row. Then you need to reload the table.
After you've done that, you should be able to scroll the tableview to the end and have the cell display properly.
The important thing to remember about tables is that they are dumb. The table itself holds no data, doesn't know how many sections and rows it has or what order the rows and sections come in. All the logic about data, sections, rows, cells and cell contents comes from the datasource and/or the delegate. When you want to change a table, you actually change the datasource and/or the delegate and then the table will reflect those changes automatically.
Edit:
Upon rereading the parent, I see that your putting the actual UITableViewCell objects in your data array and that you have one cell for each row.
This is not how tableviews are supposed to work and this will not scale beyond a few dozen rows at most.
Tableviews are intended to be an illusion that allows you display a lOGICAL table which has an arbitrary high number or rows. To that end, it only keeps enough UITableViewCell objects alive to cover the visually displayed area in the UI. With a default cell height of 44 pixels this means a tableview will never have more than 9 cell objects at a time.
Instead of eating memory holding cells that are not displayed, the tableview lets the delegate dequeue a cell that has scrolled off screen, repopulate it with the data of another LOGICAL row and then display it in a new position. This is done in cellForRowAtIndexPath:
You really need to start over here with your design. Your data needs to be kept separate from the user interface objects. You don't want to have more cells alive at anyone time than absolutely necessary because your memory use will balloon and your response time will degrade. Your current problem is the result of this unusual design.
When you've done that, you can add the result row as outlined above.
Try to scroll with some time shift after cell update via NSTimer or performSelector:withDelay:. It can help but to fix all problems I think there need to do more work.
The glitches may be caused because a UITableView considers itself the owner of any UITableViewCell instances it is displaying, and reuses them as needed. Part of that process is calling prepareForReuse on the cell. Since you are keeping the cells in an array, you do not want them reused. Try implementing an empty prepareForReuse in your UITableViewCell class. Or just create cells dynamically in tableView:cellForRowAtIndexPath: as apple recommends.
I used what Skie suggested to avoid the problem in the following way:
Immediately after adding the row:
[self performSelector:#selector(scrollToDesiredArea:) withObject:newIndexPath afterDelay:0.4f];
This called the following:
-(void)scrollToDesiredArea:(NSIndexPath *)indexPath{
UITableView *thisView = (UITableView*)self.view;
[thisView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
The delay of 0.4s seems to be sufficient to avoid the glitching; any less and it still happens. It may have to be different on varying models of iPhone hardware, though - I only tested on emulator.