i am getting large amount of data from web service. If the amount of records it return is more than 1500 or 2000 the memory overflows and the app crashes. I don't need to save the data locally, just have to present the data to the user. How can i manage it not to crash and display whole of the data?
The best way to go ahead with this would be to change the web service to accept a page param and return paginated results. To make it look seamless in a tableview, you can add infinite scrolling - i.e. if the user reaches row <last-10>, send request for the next set of records and load them in the table view.
But even with this, I am assuming you will eventually face memory issues if you persist all your 2K records (your data array) in memory. So some memory management will be needed there as well.
You can use paging in table view and also use paging for your webservice.. You can show limited data at a time and if you scroll it then show next data. Table view help you to reuse your cell and show your 2000 records
For example
http://www.oodlestechnologies.com/blogs/Pagination-in-UITableview
Related
As it's clear from the title, I have a scrolling performance issue in a table view.
Since I have read nearly every question that is posted online in this regard, and I assume all of you have much more experience with UITableView and its techniques, I won't bother with general stuff, and I just wanna point out some key things in my code that may help you help me spot where I'm doing wrong.
The UI in each cell is very very basic, so rendering each doesn't take considerable time. No shadows, no rounded corners, no extra effect, nothing. Just a few labels and two images, that's all.
The datasource is an NSArray which is already fetched from CoreData. The data of the labels are set from the content of the array, without much calculations or process required.
The height is each cell is a static integer, so the tableView:HeightForRowAtIndexPath: will immediately return the result as fast as possible. No calculations required.
The tableView:CellForRowAtIndexPath: dequeues and reuses cell with reusable identifiers so any re-creation is avoided.
So far everything is perfectly smooth. The issue is where items in Core Data are fetched from a server (Which is extremely fast) as user scrolls down. Data binding is done inside tableView:willDisplayCell:atIndexPath: to prevent tableView:CellForRowAtIndexPath: from becoming slow, as data needs to be loaded just before the cell goes live on the screen. I also fetch new items from server inside this method whenever there're some cells remaining till the last item fetched. So for example when there are totally 50 cells data fetched and put in the CoreData already and this method is called for cell number let's say 40, I request another 50 cell data from server, so that it will be ready whenever user reaches the end of the table.
As I expect this should only be called for the cells that go live on the screen. But putting some NSLogs shows that it is called multiple times until next 200 cells data are fetched (I guess the amount changes depending on device or simulator and the memory available on them and also OS limits). For example, I'm testing on an iPhone 7+, and I start the app and I go the page in which the table is. It fetches first 50 items and only first 4 items are shown on the screen, But I see that tableView:willDisplayCell:atIndexPath: is also called for cell #25, so another 50 is fetched immediately, and then it is called for cell #75, so another 50 is fetched, and this goes on for like first 200-300 cells, and then when fetching is stopped, scrolling is extremely fast and optimized until next 200-300 cells are fetched.
What can I do? Shouldn't tableView:willDisplayCell:atIndexPath: fire whenever a cell is about to be displayed? Where else should I fetch data as user scrolls?
Any ideas or suggestions is REALLY and GREATLY appreciated.
I have thousands of sections in TableView,and I use titleForHeaderInsection to give each of them a title.But I find that when the TableView is initialized, this method will be called thousands times to give every sections a title. Is that means I should set several sections each time?
No, you don't have to.
A memory is optimised for such occasions, so if your data source is properly set and contains data regardless its size, UI should handle it safely.
UITableView in iOS app, for example, is rendering just those cells, that are displayed at current time. So for example the common tableview on the iPhone 6 is displaying about 15 rows at a time.
And as a user, when you are scrolling down (or up) the table view, each time before the certain cell is displayed, the method cellForRow is called and takes data from your data source.
This also works for sections.
But, if your data don't have to be stored in the App bundle, the best way would be to get source data asynchronously, so for example you get 30 rows from data from server, after launching the app. Then if you reach (by scrolling) 30th row, your app will request more data from server and updates the table.
So user, that won't scroll your tableView, will not download all data and will not use big data transfer.
As a conclusion I would mention, that for all situations, the smaller data source, the better for performance of your app.
Tableview works on concept of reusability so only few which are visible are created and after that rest all are reused so no need to bother about it .
Rest refer to this link : Apple official link : https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/TableView_iPhone/TableViewCells/TableViewCells.html
Go through life cycle of Uitableview datasource methods as each and every method will get called for each section and row. So it is completely fine if section method is called thousand times. If you have any issue for it then add a paging like load first 10 sections and then other 10 sections and go on.
Right now my table view presents 5 cells at the same time. I load them all up into an array so the "flow" of the UITableView is easier. But since there quite a few objects, the initial load can take quite a bit.
So my question is, is there a way to present the initial 5-7 cells while the rest are loading? Or what would be the practice for this?
The idea: for the first few cells to come up as fast possible, even while we are loading a bunch in the background so the user isn't sitting there waiting for 100+ cells to load.
Also, I am loading this cells with parse (I am getting the user's info including an image).
Thanks!
This may not work depending on where your data comes from and when it gets into your app, but this is what I would do. Allow your array to fill up with the first 5-7 items. Then call reloadData on your table view. Assuming your datasource is hooked up to your array properly, it will load the first few items. Then let your array continue to load until it's complete, and call reloadData again. You could even use this strategy repetitively to continually load new data. Good luck!
I am using core data in my app to store entities that could have as many as 50k objects or more. I have this paired to an NSFetchedResultsController in a table view. The table view works fine due to cell reuse however my biggest problem is queuring the actual database to get the dataset.
When i first load the table view i need all results from the db. I am using the default fetch request with a single sort descriptor and I have set the batchSize to 1,000. On an iPad 2 this query takes up to 15 secs to finish! I also have to run this query after a search has been cancelled so overall it makes the app unusable. My assumption is that CD still has to resolve all those results or setup the sections or something, i really have no idea but just using the batchSize doesn't help?? The content is also very dynamic in the sense that new rows are always getting added, sort order changing etc.. so caching has a limited benefit.
I am thinking now that the best option would be to use a fetchLimit in the fetchRequest and then implement some basic paging. When the table view scrolls to the end fetch the next "page" of results? My only problem with this approach is that i lose the sectionIndex and i cant think of any way around that.
Anyone have any ideas or dealt with this issue already?
When you set the fetch request for the FRC the batch size should be just a few items bigger than, maybe twice the size as, the number of items that can be seen on screen at any one time. The FRC already does the pagination for you you just need to set the page size better.
s.newave,
Do your rows have variable height? If so, then the table view asks you to calculate each height and that causes every row to be fetched. 15 seconds is not an unreasonable time to fetch 50K items.
The bigger problem is your statement about not wanting to change your design. Frankly, a 50K item tableview is useless. You should change your design -- not because CD is slow, it isn't -- but because your design is not pragmatically usable.
Andrew
P.S. The fetched results controller is designed for mainstream applications. a 50K table view is not a mainstream app. If you insist on keeping with a 50K table view design, you will have to make your own controller.
I have a UITableView that is populated with content that can be tagged/bookmarked by a user. My app successfully sends the bookmark info to a server-side database when content is bookmarked. I'm trying to figure the most efficient way to display a bookmarked image in the respective cell if the content has already been bookmarked by the user.
For example, a user taps a cell's bookmark and the bookmark image displays a different image to verify the bookmark. The bookmark info is sent to the database. This already works
Here's where I need help...
When the user opens the app again, the UITableView is re-populated with data. If the cell contains content that has been bookmarked, I need to display the bookmarked image in the cell. Right now, I have a database query in ViewDidLoad that pulls the user's bookmarks. The only implementation I can think of is searching through the query data in cellForRowAtIndexPath. However, I know this is going to affect my scrolling performance. Is there a better way to do this?
If you don't have millions of bookmarks, then checking the bookmark flag on each cellForRowAtIndexPath should not impact the performance much.
In my experience table view scrolling is very very efficient, and there is still plenty of processor time to do other things. Doing a simple check for a value at an index shouldn't affect it much. Perhaps the best way to approach it, would be to parse your query result and create a dedicated NSArray with ONLY the YES/NO flag at each respective index, that you can check during cellForRowAtIndexPath to make a decision whether you show the bookmarked indicator or not.
If you really need to gain as much speed as possible, you could create a C array viewDidLoad with 0/1 values at each specific index and access it array[index]. This removes ANY additional overhead that could be there in NSArray. But again - my experience shows that for these kinds of uses, the overhead is minimal.