UICollectionView with section-specific layouts and sticky headers - objective-c

I'm building a UICollectionView with two sections. Each section has its own layout - the first section is a 2-by-y grid and the second section is a 3-by-y grid. In addition to section-specific layouts, I need sticky headers (such as those incorporated by default into UITableViews).
I have already built a fully functional subclass of UICollectionViewFlowLayout that handles section-specific layouts and sticky headers appropriately. However, the solution doesn't scale well past 250+ cells in either section. I did some profiling and investigation, and the root of the problem seems to be shouldInvalidateLayoutForBoundsChange. I return YES in my subclass because of the need for dynamic calculation of my supplementary views (the headers) as the user scrolls. This causes invalidation of both supplementary view and cell layouts, meaning that the flow layout calls prepareLayout on the cells over and over despite the fact that the cell layouts don't actually require invalidation as they aren't changing. When the number of cells requiring layout every refresh cycle creeps into the hundreds or thousands, performance drops significantly.
What I've Tried
I cache the UICollectionViewLayoutAttributes for the cells upon first calculation, allowing the flow layout to reference the cache when the system calls layoutAttributesForItemAtIndexPath. The advantage of caching in this way flies out the window when I'm constantly invalidating the layout, as prepareLayout runs it back and repopulates the cache every time. I tried implementing a system in which prepareLayout would only populate the cache on the first pass and execute no logic on subsequent passes. This dramatically improved performance, and worked, but the solution breaks down when inserting and deleting cells, causing an assertion failure.
I've also done a significant amount of research into other implementations of sticky headers and similar layout requirements involving consistent invalidation, but none of these solutions need to simultaneously tackle the section-specific layout problem that I have. As a result, their recommended solutions aren't feasible. I'm a bit out of my depths on this one...

For anyone interested in implementing something similar, here's the solution I arrived at. This solution avoids the need to default shouldInvalidateLayoutForBoundsChange to YES, saving a ton of CPU overhead, while keeping the sticky headers intact.
I manually added my headers as subviews of self.collectionView and wrote a method to track self.collectionView.contentOffset.y to float and stick the headers appropriately as content scrolls. The one caveat of this solution is that it doesn't scale well to larger numbers of sections; by adding the views manually, you lose some benefits of UICollectionReusableView such as dequeuing and recycling. In my implementation there are a maximum of 5 sections, so the sacrifice was minimal.

Related

Do we have to do two image layout transitions when creating a new image?

When I want to upload an image to device local memory I first create an image, then I issue a layout transition to transition from UNDEFINED to TRANSFER DESTINATION, then I do a copy buffer to image. Then I transition from TRANSFER DESTINATION to whatever layout I want. Is there a more direct way to do this? In vkCmdCopyBufferToImage there is an argument 'dstImageLayout'. I made the mistake of thinking that the argument tells Vulkan to transition the image automatically to that layout as it copies it. This 'would' seem to me to be more efficient and make more sense, but it's not what I thought it was.
Is there a way to do this without two layout transitions? It's OK if there isn't, I think this is the proper way to do it, I just wanted to make sure.
You do not strictly speaking have to perform two layout transitions. The GENERAL layout can be used with basically anything. So you could just transition it once, copy into it, and use it from there.
However, this would be pointless for several reasons. First, it's reasonable to assume that any layout transition from UNDEFINED will be a no-op as far as actual GPU processing is concerned. Such transitions conceptually trash any of the contents of the image, so there's no point in having the GPU do anything to the image's bytes.
Second, in order to use an image you copied into, you will need some kind of explicit synchronization between the copy operation and the usage of it. Whatever that synchronization is, it may as well include a layout transition. The GPU is going to have to make sure the two don't overlap, so you may as well toss in a layout transition.
Lastly, using GENERAL like this is a premature optimization and therefore should be avoided unless you have profiling data telling you that layout transitions are an actual performance problem (or you have no other choice).
LAYOUT_TRANSFER_DST is by definition the most efficient target for copies. So no other layout can be more efficient.
Some actual GPU might perform no actual layout transitions. The layout system is just a general API abstraction. It is not even defined what "layout" actually is, and the GPU driver may use the API concept whichever way it is beneficial for it.
If a particular picky GPU needs the image in such specific layout when copying into it, then there's no way around it, and there would be two layout transitions no matter how you shape the API. If the GPU does not need it, then it will just elide the layout transitions on its own.

Custom UITableViewCell to draw or not?

I'm designing whole UI using storyboard and all is well but for table view cells it seems to be too much. Get's crowded in designer and views can vary good deal which calls for many outlets.
I decided to custom draw them. I understand process of doing it but 2 things bother me:
Performance. Will custom-drawn cells be slower than storyboarded ones?
Reuse. I understand how reuse works and it seems that completely "erasing" contents and re-drawing might be slower than just creating new cell every time. Is that true?
If you look at design - to me it seems to be easer to write and maintain complete drawing through the code because of font backgrounds, colors, lines, etc.

NSTableView infinite scroll or pagination

In relation to How to determine if a user has scrolled to the end of an NSTableView
Thanks Josh.
Is there a way to use this mechanism to implement a NSTableView that provides some sort of infinite scroll or pagination.
The idea is to tell NSTableView to load up to a certain number of records, say 1k records at once and than as user scrolls closer to the end pull another 1k records and maybe forget the first 1k records.
This pattern is well defined/used in web applications and java. Only the visible number of rows is loaded initially and the rest is pulled async as user scrolls up and down the table.
I am interested in some obj-c code or tips on how to code this.
I know about filtering/limiting the number of records that go into the tableview but lets ignore that for a moment.
Thanks.
Given the details you've provided, I'll generalize a bit but here's how I might solve it:
First, I'd set a MUCH SMALLER batch size than 1000 records. If the result count or "the most anybody is ever going to want to see" is indeterminate (and it sounds like it is in your case), the user probably doesn't even care past the first 100 or so. If your user often requests a large, expensive list and immediately wants to see stuff so far away from the beginning they hurl the scroller downward for two minutes straight before they stop and look around, perhaps a more intuitive sort order is needed instead of asking Google Image for 1000 more animated kitten gifs. ;-)
The controller behind the (definitely view-based for view reuse) table view will need some sort of request queue since I assume you're batching things in because they're expensive to retrieve individually. This will manage the asynchronous requesting/okay-now-it's-loaded machinery (I know that's vague but more detail is needed to get more specific). You'll make sure any "currently alive" views will somehow get this "it's ready" notification and will go from some "busy" UI state to displaying the ready item (since we NEVER want to keep the table waiting for a ready-to-display view for the object at a given row, so the view should at least show some "still waiting for details" indication so quick scrolls over lots of rows won't stall anything).
Using a view-based NSTableView and associated data source methods will let the table view handle only keeping enough copies of your custom NSTableCellView around to reuse during scrolling. Since you have to provide a configured view when asked, the view's default state can either be "draw nothing if not ready" or some visually generic placeholder until the object is realized and ready (then you can just reload that row instead of the whole table). This way the table keeps scrolling and drawing rapidly because it doesn't care about what your controller is doing to fulfill the promise of updating the visible rows (that custom cell view of yours will observe its represented object's updates).
You probably want the scrollers to reflect the total number of rows batched in so far if the upper bound is astronomical - reflecting that size would make the scroll grip both tiny and very sensitive. Instead, just grow the scroller (via the table view's row count) by what the user has "requested" so far, all the way back to the beginning of the list. Any time more are batched in, you'll want to add the batch size to your controller's total batched row count. This still lets the scroller zoom by rows the user couldn't distinguish at that speed anyway. You communicate the row count change to the table view by sending it -noteNumberOfRowsChanged and replying to its resulting data source request ( -numberOfRowsInTableView: ) with the updated total row count you stashed in a property of your controller. It'll ask for views for the newly visible rows as needed (which will be in some neutral, unfulfilled visual state until it's realized as before), update the scroll view, lather, rinse, repeat.
You could use NSCache to keep memory usage low. Set its countLimit to several times your batch size and let it drop previous batches if it decides it needs to dump the first n model objects, then batch them back in if the table view suddenly asks for a view for a row no longer in the batch window's range.
Without knowing more about your requirements and architecture, it's hard to get more specific. If I haven't hit the mark, consider editing your question to include more detail. If I'm totally off base from what you're asking for, please clarify. :-)
I know more about iOS, but I think the answer is similar. Table views are intrinsically finite, but you can roll your own custom scroll view to do this. The trick is to set a large content size and implement layout in your subclass (which will get called on every scroll change). In that method, check to see if the content offset is near zero or near the content size. If it is, then translate the content offset back to the center of the content size and translate all the subviews (keep them on one parent content view) by the same distance so the user doesn't see any motion. Make a datasource protocol and keep asking your datasource for "cells" that tile the visible part of the view.
It should be up to the datasource to recognize what we would have called a page-fault in the olden days, to decide that some of the model in memory should be discarded in favor of the model where the user is scrolling.
I poked around for an NS equivalent, but didn't see one on cursory search. Here's a decent-looking reference on the idea done in iOS.

Best approach to implement asynchronous loading of photos in UITableView

I am implementing a UITableView with two images in a cell. Both images will be obtained via URL. I was wondering what is the best approach to load both these images asynchronously.
There were a couple of recommendations:
http://www.hollance.com/2011/03/mhlazytableimages-efficiently-load-images-for-large-tables/
and
http://www.markj.net/iphone-asynchronous-table-image/
However, I am not very sure which is the best approach for my requirements
1) Loading of around two hundred cells in a UITableView
2) Each cell contains two images to be obtained via URL
Those links should be useful. One good thing about UITableViews is that they do not create all of the cells at once. This means that if the images start loading only when applicable cells are created, it'll roughly load the ones on the screen first, which is a desirable behavior. If you make sure that the images are only retained by the UIImageViews then you won't risk having all images cached in memory in a low memory situation, as cells can be recreated. Depending on what kind of data you are loading, you may wish to cache the images – sized to their target dimensions – in the filesystem or database.

Simple, but dynamically generated flow chart or process chart view for iOS

Perhaps "flow chart" or "process chart" isn't even the correct terminology for what I'm looking for, but it's the best analog I can come up with. Basically, I'm trying to find a library or class that allows for the dynamic creation (in code) of connected cells/UIViews within a given space. In code, you could add/delete ordered cells from the view and it will arrange accordingly. Normally, if the superview size permits (i.e. iPad), it would arrange these connected cells horizontally. If it's space constrained (iPhone), it would arrange as many cells as possible on one line horizontally, then continue the rest of the cells horizontally below ... akin to a graphical "word wrap".
Granted, I doubt there's a magical library that does all of this, but if the SO community can point me to some better terminology and/or some potential candidates to fork, I would be incredibly appreciative.
I've looked at AQGridView and it is such a vast library, I believe it's overkill with a compiled size of +700 Kb. SSCollectionView is really close, but you have to manually center cells and it doesn't yet support variable cell height/width.
To give you a better sense of what I'm imagining, here's a pic:
Done. I had to write my own, but it works just like I wanted it to. Feel free to fork my AppendingFlowView repository at GitHub.
It's dynamic (add stages on demand).
It responds to changes in the master view by reorienting and resizing the cells as necessary with animation!
It handles multiple rows automatically, depending on the desired cell size and number of cells.
I created this open-source ios-lib to easily create a graph or tree and draw it in a view.
Please feel free to make pull requests :)
https://github.com/chikuba/JENTreeView