UIScrollView Lazy Loading Strategy With a Twist - iphone

I have a horizontally scrolling UIScrollview with paging enabled. Each page represents a data feed. Each data feed consumes a fairly large amount of memory including text and images displayed in a UITableView. Since the user can have an unlimited amount of data feeds, I need to lazy load them to prevent maxing out my memory usage. My thought is to keep up to 5 data feeds in memory at any given point, and release anything outside of that range. My initial take is to keep the page in the viewport in memory, and the 2 pages to either side of it. This way when the user scrolls, the next sequential page will always be in memory and will display quickly.
Here is my problem:
We also need to support a scenario where a user can skip to a specific data feed, possibly 10 or more pages to the right or left, which throws my entire lazy loading scheme out the window.
Might there be a better strategy to support this scenario?

Yes, what you can do is create an outer scroll view, which houses individual cells akin to a tableview. In this respect, the cells have a content view which you can place your data, let's just assume you know how to do that since it seems you do.
Once you have this architecture in your mind, it becomes fairly clear: You can, knowing the width of your cells, and the size of the screen, some simple math can tell you how many you have on screen, and you can add one to the left or right so you're preloading some data for when the user scrolls.
This will say, give you the ability to have in memory, at most 5 feeds, if 3 are visible, at the start or end of the content view in the scrollview, 4 feeds, regardless of whether or not you have a billion feeds.
One critical part of this is cell reuse. You maintain a couple NSSets, one for recycled cells, and one for visible cells. Add items that have gone off screen to the recycled cells, dequeue items from recycled cells when setting up the cell, as to save additional memory allocations, which can be expensive. Just remember, using this strategy, you are still subjective to the same caveats as a UITableView with respect to cell reuse.
I'm plugging some software I wrote here, so forgive me for that, but I'm doing so as an example of what I'm talking about if you get stuck implementing what I discussed here, it's available here for your perusal.
One final note. To support skipping of cells, it's just simple math again to adjust the point in the scrollview you are at. The action for this can be done with a gesture, though now we're talking a pan gesture recognizer, with some specific properties that will be specific to your application, as an example. Or you can use buttons if you really must. Just ensure you know how to calculate your offsets to your cells, and scroll to that point. You'll be fine.

It's hard to give accurate advice with the information provided in the question. So I will just present a few thoughts I got when reading the question.
Does it take long to download a feed? Would it be possible to just download a few item (a screenful) to get the first few items showing right away? Perhaps lazy load the images if possible.
Have you considered caching the feed data on disk? That way you can present some data right away and then update the feed as new data gets downloaded.
Since each page is a feed view, would the user just scroll past a feed without looking at it? Do you really need to load 2 views on each side or can you get away with just 1 view on each side.
I don't think your lazy loading scheme stops working just because the user can skip directly to any page. You would have to start loading the data for that page when it is selected but by using a disk cache or downloading a small sample could make the app feel faster. I also think that using some kind of animation to animate to the selected page could buy you a little time.

Here is my problem: We also need to support a scenario where a user can skip to a specific data feed, possibly 10 or more pages to the right or left, which throws my entire lazy loading scheme out the window.
Then you need to fix your data structure. I wrote pretty much exactly this (a UITableView-like paging scroll view) and it works fine (there are some reloading quirks, but hey...).
Keep track of which pages you've loaded. I used a ring-buffer-alike modulo 8 and spent a while getting the code right; it's probably a lot easier to just use an NSDictionary with NSNumber keys and UIView values. This means when you change from page 1 to 10, you can still tell that the loaded views are for pages 1, 2, and 3, and drop them.
Load the required pages in -layoutSubviews. I would load 1 or 3; 5 is probably too many (but you need to load at least 2 if you're between pages).
Load 3 (one to either side) in -scrollViewDidScroll:. This is so that when you scroll from page 1 to 2, it doesn't try to load page 4 (which hasn't been loaded yet).
Load 5 (two to either side) in –scrollViewDidEndDragging:willDecelerate: if willDecelerate is NO, –scrollViewDidEndDecelerating:, –scrollViewDidScrollToTop:, and –scrollViewDidEndScrollingAnimation:. The idea is that the animation's over, so you can load the extra views without the user perceiving lag.
Possibly load 5 in –scrollViewWillBeginDragging:, to make sure that page 3 is loaded if you're currently on page 1.
I decided to do "two pages either side" because when you flick from page 1 to page 2, it often shows a pixel-wide sliver of page 3 when it bounces. This can be avoided with scroll view insets, but we didn't think of that at the time (oops). There's otherwise not that much of a reason to choose 5 over 3.
It's a bit of work to get right, but otherwise seems to work flawlessly.

Related

Bi-directional progressive loading in tabulator

I'm looking to load a large quantity of data within tabulator and running into performance issues. Having only the small subset of records that are visible loaded would be ideal.
Reading through the documentation I could use progressive loading with an ajaxurl to load data as the user scrolls down the table with ajaxProgressiveLoad set to scroll.
Is there any way to discard the data that the user has scrolled past to keep the memory footprint low. This would also require the ability to reload it if they scroll upwards. Essentially a bidirectional infinite scroll.
I presume this could be implemented by modifying the existing ajax module, would appreciate any pointers in the right direction.

How to get rid of the hitch when lazy loading pages of a UIScrollView

I tried to implement the lazy loading solution for UIScrollView with paging enabled, as in the PageControl example from Apple. It seems to work fine, the only problem is that as the user scrolls past the 50% of the page, there is this short hitch as the content of the next page is loaded (obviously because loading the next ViewController takes some time and it seems to happen on the main thread).
Is there some way to make the scrolling seem more smooth that would work no matter how fast the user scrolls ?
You need to make sure that anything which takes time happens asynchronously. The techniques for this will vary based on what kind of content you're loading or what sort of drawing you're doing that causes the delays. Try to load images in the background, do custom drawing in the background, use operations or gcd to break up large tasks into smaller chunks that can happen concurrently, etc.
You should be lazy loading the surrounding pages so that they're already loaded when the user scrolls.
So if the user scrolls to page 2, load pages 1 and 3 (if they aren't already)

iOS - dual list layout

I am developing an iOS application and am having some issues deciding how to approach a problem.
I am using two UITableViewControllers to display different views of the same data. One is a master list, and the other only contains items which are marked as "favourite". Also the items are variable height, so I am using "heightForRowAtIndexPath" to indicate the height for each item. The problem is speed, when I switch from one view to another, it needs to be updated to display the changes made in the other (marked favourite/unfavourite).
Solution #1:
Reload the data each time a table view becomes visible. This does not work nicely because although the data is display using lazy loading, the "heightForRowAtIndexPath" is called for every item before ANY data is loaded, and its slow. on my iPhone 4 a list of about 300 items takes about four seconds to load, even if the height values are cached (the bottleneck is applying the height, not retrieving it).
 
Solution #2:
Manually manipulate the tables when changes are made. I have not tried this, but it would likely be buggy. Your thoughts?
 
Solution #3:
Using a notification type system to notify the other table of updates to items that might currently be loaded. I have not tried this, because it seems over the top and might not work at all.
Does anyone know of an easy way to show two views of the same data?
You can reload n rows with reloadRowsAtIndexPaths:withRowAnimation. I never did it, but I think it's there because it's faster, so you might want to try it out.
On the heightForRow: thingy, I can remember reading in the Apple docs that that function is indeed a killer for performance. Perhaps you could take the maximum height of the rows, and set that in the UITableView.rowHeight?

Appropriate control for page like swiping

I currently have a UIScrollView which has a content size equal to about 50 pages, each one the size of the application view.
I have implemented scrolling by using paging and at all time keep current, previous and next page in memory, while the rest are created when required, e.g. when one swipes forward, the old 'previous' view is released and a new 'next' view is loaded.
The new pages are loaded and old released when 'scrollViewDidEndDeaccelerate' is called.
The disadvantage with this is of course that the page needs to settle completely before a new page swipe may begin.
Is there a more efficient way of doing this with a different type of control? UITableView?
I am looking for solutions with other type of controls - not UIScrollView implementations with e.g. placeholder images and loading high res on demand.
Sounds like you are on the right track as predictive caching is about the only idea I can offer...which you are doing. Maybe checking available memory and loading to a certain percentage of that might help, with caveat that you monitor the low memory warning and dump items that are the farthest from the current view location.

Should I use Table View or one long chunk of HTML for iPhone?

I'm making an application for the iPhone. Essentially it'll be a guide of sorts, and all the generated information will be in one long window. Each block of information can have a 'link' in it to generate another block of connected information (for example a block about Wallace could link to a block about Gromit) that would appear at the top.
For example, at the start 1 block of data:
Wallace: Owner of Gromit
would become 2 blocks (on clicking Gromit):
Gromit: Wallace's Dog
Wallace:Owner of Gromit
Each block would also have the ability to be added to favorites list by clicking an icon. The text would need to be laid out with HTML and each block may be of a different length. A search on a different could also add a block to the top.
I'm OK with objects in 'easy' languages like PHP, but am basically new to iPhone and Cocoa, and I want to start off with the right approach here. A Table and cells looks like the correct approach, but is there any advantage of doing it as a long list (like I might do on a web version) or are there any restrictions in the way cells can hold/layout information that will cause me trouble down the line.
I believe this approach is popular for dictionaries.
I'm committed to doing it the way with a single scroll for a couple of reasons. The main one is that I want the user to be able to scroll instantly back to entries they've looked at before. i.e. the single view essentially represents a history of the data they've looked at. (if it's a lot stuff can drop off the end). Each entry will be very short but there will be a lot in total. So if the user has looked at
Wallace
Gromit
The Wrong Trousers
Cheese
Penguin
And they are not looking at Wallace, a quick half second scroll takes them back to 'penguin'.
Hierarchy is the way to go on the iPhone.
Remember that the iPhone has a small screen and that users can only see a very small amount of information at anyone time. (One interface expert compared it to driving while peering down a two inch pipe with one eye.) Users can easily get lost scrolling up and down a very long list even if it has index. (That's assuming your information can be easily indexed in a form that users will instantly recognize.) It's usually easier for users to click through several views with the data in each view getting more and more specific with each level. In addition, so many apps use this hierarchal system that your users will be used to it and expect it.
System wise, its easier for the iPhone to display just one level of hierarchy at a time so your app feels more responsive. The hardware doesn't to maintain all the data in memory but just the data it needs to immediately display.
If I understand you data model correctly, you would be best off with a hierarchy of two tables and a detail view. The first table would have an list of letters A-Z. The second table would be list of all records starting with that letter. The third would be a detail view showing links to that record. So, to see the example in the OP, a user would select W-->Wallace-->(Detail) Gromit.
Edit01:
I think you should do a test scroll of either a very long web page or UIScrollView and see how it affects performance and usability. I would caution you that layouts that seem perfectly usable and fast on laptop or desktop hardware become unusable and slow on mobiles with their weaker processors and much smaller screens. It's much more difficult to do " a quick half second scroll" back to a specific point on a long page on a mobile than on a larger screen.
You do have the option of creating a outline-like table view that inserts new indented cells as needed. I still think hierarchy is the quickest and most usable layout on a mobile.