My table cells consist of a UIView on the left side, a label in the center, and a label on the right.
I add new rows with
insertRowsAtIndexPaths:withRowAnimation:
The rows I add look just fine. But when each is added, an existing cell loses its UIView, but keeps both labels.
What could be causing this? The views keep a positive retain count the entire time, and the cell identifiers are all unique. Also, when I scroll down and then up, so that the cells have to be redrawn, the UIViews are again visible. When the row is added, cellForRowAtIndexPath is definitely only called on the new row.
I would really check the memory/retain logic. By the way, how are you sure that your retain count is correct? DO NOT RELY in retain count since it does not do as you expect it. I tried many times before and it is not reliable and therefore even often missleading.
Do you use ARC already?
Are you by any chance reusing the same variables (directly or indirectly) to create your UIiew?
Just for testing purposes make sure you really retain the views (for example add them to an mutablearray or so) and see what happens.
Also if you post some code, it will be much easier for us to help!
Related
Right, so my UITableView loads and puts all the cells in Alphabetical order. Information is then downloaded from a server and calculations are done, with the TableView being reloaded once everything is complete. Currently this is quite a simple procedure as once the information is downloaded from the server, the cells don't even move, they are left in their alphabetical order. Nothing really happens other than half of the information is filled in and small changes are made depending on the calculations. I was wondering if there was an easy way of putting the cells into sections depending on the calculations done after the download is complete? I did have an idea of creating 4 arrays (there will only be 4 sections ever) and once isLoading is set to no, changing the data source of the TableView to have sections, however, that sounds a bit... iffy. I know this is a theoretical question as opposed to a coding problem, but before I go and mess up my code, in what is sure to be a stupidly inefficient way of doing things, is there an easy way of "assigning" UITableViewCells to sections?
My main issue with my way of doing it is that should the user delete a cell, deleting the appropriate entry in Core Data will be a little tricky and prone to errors. This lead me on to another idea. What if I added an extra attribute to my Core Data entity. That attribute would be assigned and then saved once the calculations were done. The problem with this is that no existing databases would work. There has to be a neat way of achieving this.
Thanks for the help. If you need me to post any code just say so and I will.
You should be fine if you implement the data source methods related to sections.
For example:
numberOfSectionsInTableView
sectionIndexTitlesForTableView.
Any time the table data is reloaded (e.g., [self.tableView reloadData]), these methods will be called and the data will be placed into their sections.
Keep in mind that the cells are just the visual representation of your model, which in this case is your fetched data. The cells are not assigned to sections; they are simply created however you specify for your model (via the table view data source and delegate methods).
Regarding deletion of entries while using Core Data, I suggest taking a look at NSFetchedResultsController. The latter will monitor any changes to your table's data and message its delegate, your table view controller, when updates are made.
For example, a deletion would start with a call to the table view delegate like normal (i.e., via tableView:didEndEditingRowAtIndexPath). Within the latter, you would then delete the entry from core data (e.g., [self.myDatabase.managedObjectContext deleteObject:entity]). Assuming you initiated the NSFetchedResultsController w/ the same managed object context, the deletion would be automatically reflected back to your user.
If you're using a remote DB, however, you'll also have to perform a save (however you've implemented that) to ensure the DB is updated too.
Note also that if you use an NSFetchedResultsController, you don't need to implement the section data source methods since NSFetchedResultsController can handle that for you. Just define the key-path in your data model that will return the section name when initializing the NSFetchedResultsController.
I have a UITableView which reloads every 3 seconds. I reload it only if my Boolean variable is true. In some conditions that variable is always true and that time table is difficult to scroll. (performance is not very good). Other times its ok. What should I do?
Note: I have coded my table according to apple's recommended way(UITableView best practises. Except I add subviews to UITableViewCell, instead drawing on it. I'm confident with other techniques).
What's the solution for this?
Are you sure that because you refresh a lot that makes you difficult to scroll. What kinds of refreshing you mean here, refreshing data from network or refresh the table view your cell.
Everytime you refresh your table view or when you scroll the table view, the table view will keep asking for the corresponding cell, and now the whole performance depends on how fast you return the cell.
If you are doing custom UITableViewCell, you are in risk of having a slow performance. Double check these things:
Do you reuse your cell correctly? Check if [tableView dequeueReusableCellWithIdentifier:] always return nil or not. If it always return nil, then you do it wrongly.
Check if you block the main thread somewhere by loading images from network or file, if it is, using multithread.
After you check everything and still has a slow performance, then you either need to reduce the times a cell is returned (by less refreshing) or draw the cell yourself
If you are fetching large data set for UITableView, try to fetch the data on need basis. Get the data for each cell in cellForRowAtIndexPath instead of getting all the data for table view and storing.
I have a grouped UITableView in my UIViewController class and I'm finding that at times, the dequeued cell is not nil as expected.
The table has 3 sections to start with and and as soon as the 'viewDidLoad' is invoked, a server call is initiated to find out if there are more sections. Before the view is even rendered, the server's response arrives and we're told that we have 4 sections. To deal with this change, I do:
[self.tableview beginUpdates]
// Do the updating of the array that holds table data
[self.tableview endUpdates]
[self.tableview reloadData];
Next I get the call to 'numberOfSectionsInTableView', for which I return 4, followed by 'numberOfRowsInSection' and I'm returning the expected correct row count. Note this is the first time I'm getting either of these calls since the table reload happened so quickly and before the view was even rendered.
At this point, only the data from the 1st 3 sections are visible and scroll to view the last section. When you scroll to see the last section, one of the cells I'm expecting to be in the call to 'tableView:cellForRowAtIndexPath' is not nil as expected. The cell type is actually what was used for another section (I have one type of UITableViewCell for the 1st two sections and another for the last two sections that I'm creating and handing back in cellForRowAtIndexPath when cell is nil).
So how does UITableView figure out which cell to dequeue and how do I figure out why this cell for this particular section/row is not nil when it really should be?
What are you using for your cell identifier? That's what a UITableView will use when determining what to dequeue. If you have different types of cells, you’ll need different reuse identifiers.
The idea of cell reuse boils down to minimizing view allocations and internal UITableViewCell setup in order to improve scrolling performance. Roughly speaking, whenever a cell goes off screen, a table view removes it from itself as a subview and adds it to a pool. The next time it needs to display a cell, you can dequeue an unused cell from the pool, configure it and return it to the table view. That's a clever implementation, but a table view cannot efficiently reuse cells without your hints, so called reuse identifiers.
The most common approach is to tie reuse identifiers to cell classes. If, say, you use cells of class A to display people from an address book and cells of class B to display organizations, you obviously cannot reuse cells B for people, and vice versa. In such a case you designate these two classes different reuse identifiers, which guarantees that when you need to provide cell A, the table will either dequeue a cell A or return nil if its pool of unused cells A is empty.
A natural extension to that is nil reuse identifiers. They tell a table view that it should not reuse them at all, releasing them when no longer on screen. Why may you need it? For unique cells or for the cells which you manage yourself, or for the cells which have an untypical life cycle, or are extremely expensive to draw. However, the fewer unreusable cells you have, the lower memory footprint.
For the app I'm currently working on, one of my main ViewControllers contains two UITableViews whose contents vary based on what's stored in my model (Core Data backed by SQL). Both tableviews set their delegate and datasource properties to the shared VC they're both subviews of, and I provide the necessary methods in the VC to respond to tableview-specific events.
I'm finding that when I add new data to my database that should cause the TableViews to show new rows, and I return to the VC and call reloadData on both TableViews in the VC's viewWillAppear method, I see that numberOfRowsInSection returns the correct number of rows based on the new entries in CoreData, but cellForRowAtIndexPath is not called the correct number of times, almost like it's not responding to the change in the model data. For example, if I had 3 rows to show then the app started, and I added another entry in CoreData, then called reloadData, numberOfRowsInSection returns 4 correctly, but cellForRowAtIndexPath is only called 3 times still.
Besides the fact that I probably be using something more suitable like NSFetchedResultsController to link my TableViews to CoreData, is there any reason why cellForRowAtIndexPath is not being called the correct number of times, as dictated by the correct number of rows returned by numberOfRowsInSection?
I would put in some of my code, but it's really standard boilerplate TableView stuff and I feel like there's something else that I'm missing.
Just to make sure you understand correctly, the cellForRowAtIndexPath: is called with the number of cell that can be in the screen. If you have 4 cells, but because the height of the cell, only 3 cells can be available to users, then the cellForRowAtIndexPath: is called 3 times
If the user can only see only 3 cells, not more, then the cell loading callbacks may only be called 3 times, even if there are 100's of rows in your table.
I would like know the limit of maximum number of rows that can be shown in the UItableView. Thanks in advance.
This Cocoa with Love blog post is very informative - it looks at the performance of a table view with thousands of cells.
The question is whether it has some internal hard limit - it's seemingly enough for most needs - the above post concludes:
The iPhone can handle tables with 100,000 rows — and it continues to scroll as smoothly as though it were only 100 rows.
There seems to be no maximum. You don't insert actively anyway, you just implement the delegate methods to serve the cells - they are not loaded all at once if done properly.
Just having tried it with one thousand custom cells, it worked without a problem. But with 3k even with index it is a lot to scroll, I'd consider putting them into a navigation hierarchy (but this heavily depends on your use).
One more thing: Make sure to be efficient when drawing the cells, i.e. implement drawRect: instead of cluttering the cells with labels, views etc., this will make scrolling much faster.
You will ultimately be limited by the fact that all the methods such as numberOfRowsInSectiin take integer arguments but hopefully you won't need to approach those limits :)
In numberofRawInSection delegate method we are generally return count of array which is Int type, so maximum number of rows that can be shown on tableview's section = Int.max
So let say capacity "C"
C = (maximum number of section) X (maximum number of item in section)