iOS - dual list layout - iphone

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?

Related

XCUITest: Interacting with table with many cells

I have a UITableView with a search bar. The table can contain thousands of cells. Performance is fine, since only cells on-screen are actually fetched/rendered.
I'm trying to interact with the tableview using a UI Test, even trying to tap the search bar doesn't seem to work properly:
app.tables.searchFields["identifier"].tap()
Will cause the app to become unresponsive (100% CPU and ever-rising memory). When I do the exact same thing with only a small set of cells, it works just fine.
My guess is, some part the stack is fetching all the cells to query through.
Is there a way to prevent this?
Maybe it will help to use firstmatch
https://developer.apple.com/documentation/xctest/xcuielementtypequeryprovider/2902250-firstmatch
When you call firstMatch, XCTest stops traversing your app's accessibility hierarchy as soon as it finds a matching element, speeding up element query resolution.
Its also possible to use element(boundBy: Int):
https://developer.apple.com/documentation/xctest/xcuielementquery/1500842-element

Reasons why UITableView doesn't show scrollers

I have a NavigationController that stacks UIViewcontrollers that have UITableView among other UI elmeents. The tables I use are actually custom tables that use custom cell views. With this arrangement those tables don't show scrollbars of any kind even though I have configured in IB the Shows Vertical Scrollers to show them.
I've tried several ways of debugging this without success. If I print to console the value of this property (showsVerticalScrollers), it prints 1, so the property is properly set, and no, my table is not wider than its parent view, actually it's way more narrow than its parent view.
Are there reasons why a table won't show it's scrolls?... btw, this happens in iOS5 running in simulator. I'm using xcode 4.2 with SLeopard. I don't have access to an iPad to test it in the hardware but other tables I have in the same project, show their scroll bars without a problem.
EDIT
thanks for the answer... I did one last test and found that one of the causes for not showing a scrollbar is the number of elements shown in the table, when they fit in it without the need to actually scroll. Say if you have a table with 1 row and the vertical size of the table is too big for just one row?, then iOS won't show the scrollbar when bouncing.
It's hard to provide a possible solution without seeing any code, but the advice I can offer is to update your code to the bare minimum needed code to implement a UITableView and see if scrolling works, if it does, add in functionality in small increments, testing the scrolling with each new code addition till you reach the point that scrolling breaks. Incremental testing in this fashion helps to avoid issues like this where you are unable to effectively debug your application.

Is it a good idea to add uiwebviews in table cells?

I have several local html files and I want to show them in a table with static(for now) cells.
Each UIWebView would have a different height (I dont want to have any scrolls in the UIWebView) so obviously each cell has a different height. Most of the comments i read discourage the usage of UIWebView in table cells. What do you think : is this applicable ? will it be slow and need time to load?
UIWebview in tableview cells will affect the performance for sure. To display Html content in tableview cells you can use NSAttributedString.
I used a UIWebView in an app to display content coming from the web as rows in a table view.
I know that doing that is discouraged, due to the high memory use of UIWebView, but in my case it was not only a matter of displaying rich text: I had to display HTML loaded from the web, so I give that option a try. In short, I tried it and it worked fine.
The app got memory warnings from time to time, but it was also able to recover memory effectively and run unaffected even on an ipod touch (just 256MB of memory). Performance was possibly affected, but due to the fact that the app was doing just that (loading text from the web and displaying it in the table), the app was perfectly responsive.
One important notice: pages were pretty simple, just text, and each table did not contain more than 15 elements. Furthermore, one thing you have to consider is that the UIWebView introduces a small delay in the rendering, so you will have to deal with it and accept it in your app (there is no way to get rid of it that I know of).
In a later project, I used a different approach: using just one UIWebView and creating the table using HTML/js inside of it. Well, it also worked, but I could not see any big improvement as to peak memory usage nor performance (I checked with instruments).
This is just my experience, I am not trying to say that this will work or that is good practice to do. In any case, I hope it helps.

UIScrollView Lazy Loading Strategy With a Twist

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.

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.