IKImageBrowserView lazy loading?

915 views Asked by At

In order to work with the IKImageBrowserView, one must implement a datasource with the following methods

– numberOfItemsInImageBrowser:
– imageBrowser:itemAtIndex:

This is not dissimilar to NSTableView, which has the following datasource methods

– numberOfRowsInTableView:
– tableView:objectValueForTableColumn:row:

However, the disturbing difference is that whereas NSTableView takes into account what's currently visible before calling – tableView:objectValueForTableColumn:row:, IKImageBrowserView seems to iterate over the entire range given in – numberOfItemsInImageBrowser: and ask for imageBrowser:itemAtIndex:. Unfortunately, the datasource is sometimes backed by hundreds of thousands of items, loading all the unnecessary ones is a terrible waste. Is there anyway to make IKImageBrowserView only load the items visible, (+preloading of course) just like the NSTableView does?

Update

I tried writing a NSProxy subclass and it indeed worked. (well, more on that in a sec) It looks like this

// .h file

#import <Foundation/Foundation.h>

@interface ILArrayItemProxy : NSProxy

- (id)initWithArray:(id)array index:(NSUInteger)index;

+ (id)proxyWithArray:(id)array index:(NSUInteger)index;

@end

// .m file

#import "ILArrayItemProxy.h"

@interface ILArrayItemProxy() {
    id _array;
    NSUInteger _index;
}

@property (readonly) id target;

@end

@implementation ILArrayItemProxy

- (id)initWithArray:(id)array index:(NSUInteger)index {
    _array = array;
    _index = index;
    return self;
}

- (id)target {
    return [_array objectAtIndex:_index];
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}
+ (id)proxyWithArray:(id)array index:(NSUInteger)index {
    return [[ILArrayItemProxy alloc] initWithArray:array index:index];
}

@end

Now, when the imageBrowser asks for – imageBrowser:itemAtIndex:, I would return

[ILArrayItemProxy proxyWithArray:self.arrangedObjects index:index];

This works very well! Lazy loading is indeed achieved and I am no longer retrieving all the objects from the store at once. However, upon calling reloadData on any of the views, troubles ensue.

Here is the error message from NSTableView

Cannot update for observer <NSAutounbinderObservance 0x105889dd0> for the key path 
"objectValue.status" from <NSTableCellView 0x105886a80>, most likely because the value
 for the key "objectValue" has changed without an appropriate KVO notification being sent.
 Check the KVO-compliance of the NSTableCellView class.
2012-01-29 11:48:01.304 [62895:707] (
    0   CoreFoundation                      0x00007fff92bf6286 __exceptionPreprocess + 198
    1   libobjc.A.dylib                     0x00007fff91d3ad5e objc_exception_throw + 43
    2   CoreFoundation                      0x00007fff92bf60ba +[NSException raise:format:arguments:] + 106
    3   CoreFoundation                      0x00007fff92bf6044 +[NSException raise:format:] + 116
    4   Foundation                          0x00007fff9154b519 -[NSKeyValueNestedProperty object:withObservance:didChangeValueForKeyOrKeys:recurse:forwardingValues:] + 689
    5   Foundation                          0x00007fff9154986f NSKeyValueDidChange + 186
    6   Foundation                          0x00007fff914f60fb -[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 130
    7   AppKit                              0x00007fff90a2c26d -[NSTableRowData _addViewToRowView:atColumn:row:] + 434
    8   AppKit                              0x00007fff90a2bf81 -[NSTableRowData _addViewsToRowView:atRow:] + 200
    9   AppKit                              0x00007fff90a2abe3 -[NSTableRowData _addRowViewForVisibleRow:withPriorView:] + 404
    10  AppKit                              0x00007fff90a2a9e2 -[NSTableRowData _addRowViewForVisibleRow:withPriorRowIndex:inDictionary:withRowAnimation:] + 184
    11  AppKit                              0x00007fff90a2a928 -[NSTableRowData _addRowViewForVisibleRow:] + 38
    12  AppKit                              0x00007fff90a2a06c -[NSTableRowData _unsafeUpdateVisibleRowEntries] + 448
    13  AppKit                              0x00007fff90a29e87 -[NSTableRowData updateVisibleRowViews] + 95
    14  AppKit                              0x00007fff909c1c56 -[NSTableView viewWillDraw] + 156
    15  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    16  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    17  AppKit                              0x00007fff909254a2 -[NSScrollView viewWillDraw] + 43
    18  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    19  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    20  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    21  AppKit                              0x00007fff90924f7b -[NSSplitView viewWillDraw] + 67
    22  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    23  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    24  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    25  AppKit                              0x00007fff90924f7b -[NSSplitView viewWillDraw] + 67
    26  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    27  AppKit                              0x00007fff90924c11 -[NSView viewWillDraw] + 666
    28  AppKit                              0x00007fff90923952 -[NSView _sendViewWillDrawInRect:clipRootView:suppressRecursion:] + 1358
    29  AppKit                              0x00007fff909226c1 -[NSView displayIfNeeded] + 1039
    30  AppKit                              0x00007fff9091e6fa -[NSAnimationManager animationTimerFired:] + 2593
    31  Foundation                          0x00007fff91537014 __NSFireTimer + 102
    32  CoreFoundation                      0x00007fff92baaf84 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 20
    33  CoreFoundation                      0x00007fff92baaad6 __CFRunLoopDoTimer + 534
    34  CoreFoundation                      0x00007fff92b8b471 __CFRunLoopRun + 1617
    35  CoreFoundation                      0x00007fff92b8aae6 CFRunLoopRunSpecific + 230
    36  HIToolbox                           0x00007fff8cdf63d3 RunCurrentEventLoopInMode + 277
    37  HIToolbox                           0x00007fff8cdfd58f ReceiveNextEventCommon + 181
    38  HIToolbox                           0x00007fff8cdfd4ca BlockUntilNextEventMatchingListInMode + 62
    39  AppKit                              0x00007fff908e63f1 _DPSNextEvent + 659
    40  AppKit                              0x00007fff908e5cf5 -[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] + 135
    41  AppKit                              0x00007fff908e262d -[NSApplication run] + 470
    42  AppKit                              0x00007fff90b6180c NSApplicationMain + 867

IKImageBrowserView on the other hand would just hang on me without any kind of error message if I were to call its reloadData.

So yea, I managed to replace one problem with another, c'est la vie?

0

There are 0 answers