I think this is a new spin on an old question, but I'm completely stuck here.
In my app, I have a UITableView with 650 cells, each with a custom 16x16 RGB icon. On most recent iOS devices, loading all of those icons into memory before displaying the table works totally fine, but on older hardware, I'd like to implement a lazy load system that only loads icons it needs.
I've implemented the Apple LazyTableImages example, (which uses a UIScrollView delegate to determine when the table stops moving to load the visible icons), but I've run into another snag.
My UITableView also has a section index display (ie the list of labels on the right hand side you can swipe up and down to scroll quickly), and the LazyTableImages example hasn't taken this into account.
If I scroll using the index, the images won't lazy-load. :(
As far as I can see, the scroll index doesn't actually have any delegate events it triggers.
So I'm wondering, has anyone else tried to implement lazy-loading on a table with a scroll index? Is there any way to track the index and find out if the user has interacted with it?
Thanks!
After buzzing around a few of my iOS developer buddies, I came up with a solution that worked well enough.
I set it up so that in addition to the icons being loaded from the UIScrollView delegates, an NSTimer object will periodically call a method that checks the currently visible table cells ([UITableView indexPathsForVisibleRows]) every .5 seconds, and loads any icons on the screen that haven't been loaded yet in a single separate thread.
I tried to make the solution as efficient as possible, so I made sure the timer was only active when the tableView was visible and stationary, and I liked it since it meant that every visible icon regardless was addressed.
One thing I discovered was that if the tableView was reloaded while the thread was looping through the visible cells (rare, but was possible), it would crash. The solution to this was to make sure each cell data source entry was retained while the icon was being loaded.
Related
In Mavericks, Apple introduced responsive scrolling architecture in NSScrollView. This feature adds a smart way for generating overdraws and decouples handling of the incoming scroll events from the main event loop into a separate one. This topic is described in detail in the session 215 from WWDC 2013.
Because of the different event model, the scroll events no longer go through the scrollWheel(with:) method. In fact, if you override this method in the NSScrollView subclass, you opt-out from the responsive scrolling architecture completely and fall back to the legacy model.
In my NSScrollView I'd like to implement an interaction for magnification using scrolling while holding the command key. This can be observed in MindNode or OmniGraffle apps. The standard way to do this would be overriding scrollWheel(with:) and checking the modifierFlags on each scroll event. As described above, this would cause an opt-out from the responsive scrolling model.
I wonder whether there is a way to implement this interaction while also preserving the responsive scrolling?
What I have already achieved/tried:
In my NSScrollView subclass I have overridden scrollWheel(with:) and am returning true from the static property isCompatibleWithResponsiveScrolling in order to force the participation in responsive scrolling.
This way I am able to check the first scroll event for the modifier flags. If the command key is NOT pressed, I simply pass the event to super and let the NSScrollView do its thing. If the command key is pressed, I go a different route and track next scroll events on the window to do the magnification.
The problem is when one of these tracking loops is running and the user changes the press state of the command key (either presses it or releases it).
The switch from magnification to scrolling (on command key release) is simple, since the tracking loop is fully under my control.
The switch from scrolling to magnification (on command key press) is more tricky, because I cannot check the scroll events. I have overridden flagsChanged(with:) and can observe when this moment happens but I have not found a way to end the scrolling. This SO question asks about ending/disabling scrolling but has no answer yet.
I’m going to answer here instead of in comments, since I have more data.
There are two issues with “Responsive scrolling”:
(1) A few years ago there was a bug — a difference in how accelerated responsive scrolling was vs. legacy scrolling. By accelerated I mean the distance the document would travel for any given amount of movement of the fingers on the trackpad — legacy scrolling didn’t move the document as much so it felt sluggish. I reported this and it seems to have been fixed in 10.14, at least.
⑵ As near as I can tell from my research and talking to Apple people “Responsive scrolling” was designed to make scrolling more consistent even when an application is hogging the main thread (which would normally block the flow of events) — it processes scroll wheel events on a background thread instead and then messages the main thread with them. Since this still involves the main thread being called per scrolling event (or scrolling events being coalesced) I’m not clear what situations this is a win in. There may be some extra magic where the scrollview will draw more of the document from the cached content of you implement the optional caching stuff.
In my app I’m actually using the scrollview with a dummy (transparent) document and I take its scroll events and move around a SceneKit camera. Since my scene changes dramatically as you scroll around there’s no benefit to trying to pre-render outside the visible rect.
So in my case I no longer perceive any performance difference between responsive and legacy scrolling (although I did back when I filed the bug).
It seems that my view-based OutlineView try to re-render text every time user scrolling. I think that was because it tried to save memory by re-using rows that fall outside of visible rectangle, then re-render new data to those old views. How can I disable this? I'm willing to sacrifice memory to archive this so that I can get better scrolling performance. (I have tried with various optimize like layer or so, but no luck).
EDIT: There is another question with same purpose as mine here How to make NSTableView not reuse TableCellViews, but no answer yet :(
I think you are confused -- it doesn't re-render the text. The table only shows views for the visible area (caveat below), and pulls in new views as you scroll -- potentially re-using old views.
You provide the view. Use the delegate method viewForTableColumn:row: and return your own cached view for a given row.
But that isn't going to prevent drawing; the view will likely still get marked dirty and draw.
I don't think you are asking the right question. It sounds like you have a performance problem. To help you with that, we'd have to see samples or instrument traces.
Caveat: Responsive Scrolling will pull in views that are in the non visible area. See the AppKit release notes about this.
corbin
I am displaying hundreds of thumbnails in my view . I know default way to handle tap on thumbnail is using UICollectionView delegate method "didSelectItemAtIndexPath" but since its many thumbnails i wanted to look into adding gestures to the screen position so when i tap on a particular spot on the screen, it will handle the event accordingly for that particular thumbnail underneath. I would like to know if it is a good/possible approach?
It would be a hell of a lot easier to use a UICollectionView.
If you need a custom layout then you can subclass UICollectionViewLayout and get some really cool dynamic layouts.
You also get the added bonus of dequeued cells meaning that you get better memory management using it.
You may find UIGestureRecognizer useful. A good tutorial to get you started is here.
Initially I was under the impression that it uses the table row slideup/down animations while inserting/deleting new rows but I doubt if it's doing that as it does it so fluidly even with thousands of items in the list (otherwise it would take a lot of time for the deletions/insertions to work).
Am I right in my assumption that it's simply attaching a new instance of the News list at the bottom of the screen, shrinking the above one while the one at the bottom expands to fill up space?
UPDATE:
Please see this video of what I mean: http://dl.dropbox.com/u/4960327/ReederAnim.mov
I can not tell you exactly how Silvio Rizzi made this, but as you see in the playback, a list view is added behind the shown list view, and the front list view fades out (.alpha = 0.0;) while the list view behind it expands its height per row.
When you desicate it frame by frame it becomes quite clear what he does, and it is really not that advanced. But I have to admit, with the white "milky" polished interface, it looks quite neat.
In addition, you can see that while animating, the background list view only renders the top 7 entries (hopefully calculated by dividing the view height with the average height of the cells shown) making the list view quick to load. Then afterwards, he can load an extended array of cells once you start scrolling, or in a background thread starting once the animation is complete.
I am relatively new to Objective-C and iOS development.
What I want to do is have it that I have an array of length 52 representing a deck of cards, with each value in the array corresponding to a card image. When a button is pressed, the card that comes out will appear in an image view (already done), as well as appear as the right most element in a horizontal scroll view.
I do not however know how to do this in Objective-C, neither the horizontal view nor the appending to the end (or right).
Any help will be much appreciated,
Thank you very much for your time.
AB
What you're looking to do is called Paging with a UIScrollView. Download the "Page Control" Sample project by apple here :
http://developer.apple.com/library/ios/#samplecode/PageControl/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007795
Think of each page in that project as another card. You can add/subtract from it whilst the program is running. You can take a look at my open source "Dollar Bets" app on github for another example here:
https://github.com/Rich86man/Dollar-Bets
Check out MainViewController