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.
Related
I am writing a new app in swift and I'm new to swift was well.
I have setup each of the cells in the tableView so they ask for a specific type of answer. Things like multiple choice, Yes/No, Quantity and Photo. I have the answers in the cells as I can log them out when we make changes to things like the yes/no questions. But my problem is I need to get each question, its type and the response the user gives to serialize in JSON to send back What is the best way for me to get this data so I can send it?
Thanks in advance for everyones help
Just a short overview, So you get your answer
UITableView is highly optimized, and thus only keep On-screen visible rows in memory. Now, All rows Cells are cached in Pool and are reused and not regenerated. Whenever, user scrolls the UITableView, it adds the just-hidden rows in Pool and reuses them for next to be visible rows.
So, now, coming to your answer
UITableViewCells won't save user selected values
Best solution will be to pro-actively save user selected response in NSMutableDictionary for each index.
For eg., if user taps on yes, then save that in datasource on tap event and keep updating your cell view in
tableView cellForRowAtIndexpath
And send the same saved data to server.
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
For an app I am creating I am looking for the correct way to display a medium sized records set (around 130 records, with about 13 columns). The data is now in a sqlite database, which I want to use to display in a UITableView. With this quantity of records and columns I think an array is way too memory intensive?
From looking around (and previous experiences) I think I best go for Core Data and use an NSFetchedResultsController, but I am not sure about this. There is no need for the data to be changed or added. So using this might be overkill?
If this is the way to go though, I would love to know what components to use, as I cannot really find an answer to this sort of question.
If this is not the way to go, please point me in the right direction in which I should go.
Core Data is a good match. It can also be used with sqlite as a datastore however you would probably have to reimport your data. The NSFetchedResultsController makes it fairly easy to use a core data collection as a datasource for the table-view. The modeling tools in XCode make it also pretty convenient to create your data structure and creating the NSManagedObject subclasses from it. I would also take into consideration that you might want to extend your dataset or structure in the future and core data has battle tested facilities to assist you with these tasks.
There is a little trick, you needn't select all data from this table and transform into an array, just select data like this:
SELECT `recordid` FROM `yourtable`;
in the delegate method "tableView:cellForRowAtIndexPath:", you can now load all rest data from this record:
SELECT * FROM `yourtable` WHERE `recordid` = [indexPath row];
If your database records is still growing, you can fetch your data by paging query:
SELECT * FROM `yourtable` LIMIT 10 OFFSET 0;
The answer depends on what you need. In particular, if you have to release the application in a day or so, well, refactoring to Core Data would be an obstacle. It has a very steep learning curve so it cannot be digested in a day. On the contrary if you have more time to spend, yes go with Core Data and reuse the know-how in the future.
So, stick with two options here. Stay with plain SQL or just migrate to Core Data. Well, if you have chosen the second, the master component you need is the NSFetchedResultsController.
This class works in combination with UITableView offering a lot of functionalities. One of the most important is to maintain a small memory footprint. In fact, if you work with batch sizes you can take advantage of lazy loading mechanism. Say for example, you want a batch of 20 items. The NSFetchedResultsController will load the first 20 items, if you scroll down, it will load other 20 and so on.
The main problem will be not how to organize your internal data structures, but how effectively present 13 columns of to the user, taking into account very little screen real estate you have on iPhone. Have you thought about that?
There are few options here
you can present just some kind of brief information into main table view and for each cell have detail view with the rest of the information
you can dynamically generate bunch of labels inside each cell and populate them with your data, but now you're really facing space limitation, since you probably won't fit that much of a data in one row. And you also need to create custom headers in your table view (if you need to show header)
or you can invent something else. When I had to deal with very similar issue I went and implemented something similar to Excel "freeze columns" functionality - where header columns is fixed and stays on the screen always and all data columns can be scrolled horizontally and basically go underneath the header. See screenshot attached (note scroll bars around columns with stat data):
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 am developing an iOS application that mainly use a UITableView.
It retrieves pages of articles from a server. We have got >25000 articles; so I have implemented a pull-to-refresh and infinite scrolling to travel across the title collection.
The ones downloaded persist using core data; NSFetchedResultsController is used to automatically update the UITableView.
However, I have implemented the infinite scrolling to be in both direction; up and down. Since the user will be allow to scroll down the 25000 article titles, I have to remove the one that the user has already scrolled. If the user scrolls up, I have to re-insert title above the current one.
Doing so, I have got a moving window inside the article collection.
I display my articles by date group using the section and header.
The problem is that because the infinite scrolling goes in both direction, I often delete or add article at a higher position in the table.
Infact to achieve that, I have got to change the predicate associated to NSFetchedResultsController.
This result by scroll being messed up. I have made it jump back to the position where it was supposed to be ish. It is not nice because it is a jump (animation:NO), if I put the animation ON, it goes all around the place before going back to the right position. And the position is not exactly the one it should be.
I am not sure I well explained my problem. I think the problem might be in the way I am using the UITableView but I am not sure how I should use it to make this better.
Cheers
If you only store article titles in Core Data, I think it can handle 25.000 titles.
However I think that the problem you've got is with the pagination of the visible elements of the table
Here is a link that I used to handle something that you requested. You'll have to tweak a little bit to use Core Data and a remote source.
Please post some code if you can so we can have a look.