Cocoa - NSScrollView with large number of items - cocoa

I have an NSScrollView to which I'm adding a bunch of NSViews that have an NSImage subview. Basically it is a long row of thumbnails contained in a scroll view.
Everything works great... until there are thousands of images in the scroll view (since there is no creation of separate thumbnail images, the images are large but downsized to thumbnail size).
It seems the best thing to do would be to dealloc the images that aren't currently shown in the scroll view, and load them back into memory as they come into view. Does NSView support this type of notification (similar to viewWillAppear: and viewWillDisappear: on iOS).
Also, in ARC mode, how do explicitly tell the OS to unload an image? Will setting the NSImage* to nil do the trick?

I think the best approach would be to create a custom NSView subclass that owns the image subviews:
Code your drawRect: method so that only those images that intersect with the dirtyRect are drawn.
Embed your custom view as the subview of the NSScrollView.
As images are added/removed (if this is even possible) you'll want to recalculate your view size and call [super setFrameSize:] so the scroll view knows to change the scroll bar length etc.
Override setFrameSize to re-layout and modify the size, on the way through, if you change the layout when the view is resized (if you have the concept of layout in your view).
You don't say in your question what issues you are facing; are they memory or performance (or both)? I don't think continually releasing and reallocating the subimages will help you either way.

Related

Display UIImageView contained in UITableViewCell fullscreen with animation when tapped

I've an UIImageView inside an custom UITableViewCell and want it to display fullscreen when I tap on it. I've attached a UITapGestureRecognizer to the UIImageView and the image already resizes. The problem I have right now is that the image only moves inside the cell it is contained in, but it should in fact move out of it. I'd also like to put a black background behind the image.
Any ideas on how to achieve that?
I would say the culprit is most likely either the cell, or the cells content view is clipsToBounds enabled. Setting [cell setClipsToBounds:NO]; should solve this problem.
However, if you're willing to take a new approach to this, it may be beneficial to instead of growing the cells imageView, add a new image above the tableview in the same location as the cell's image view and then grow that. This way you wouldn't have any undesirable behavior like being able to scroll the image view away because the table is still scrollable.

How to place an NSButton over an NSTableView

1) No table behind button
2) Table loaded
3) After scrolling
If I place a button over an NSTableView I get artifacts being left behind after scrolling. Does anyone know how to fix this?
My current solution is just to split the section with the table into 2. The lower portion is a disabled button in the background.
Try the NSScrollView method
- (void)addFloatingSubview:(NSView *)view forAxis:(NSEventGestureAxis)axis
All tableviews should normally be inside a ScrollView.
I had a similar problem and solved it, see Why does an NSTextField leave artifacts over an NSTableView when the table scrolls?. Essentially OSX will draw the contents of the table and let the parent scroll view move the cached contents avoiding redraws to be performant. By subclassing the parent scroll view it can be forced to refresh the table hooking the reflectScrolledClipView: method. Then the whole table, including overlays, will be redrawn with each scroll.
That's because the scroll view is set to copy its contents when scrolling and only redraw the newly-uncovered part as a performance optimization. To turn that off, use
myTableView.enclosingScrollView.contentView.copiesOnScroll = NO;
though that will make scrolling use more CPU (you can also do this in the XIB, look for a 'copies on scroll' checkbox).
Probably a better approach would be to switch the scroll view and the button to be layer-backed:
myTableView.enclosingScrollView.wantsLayer = YES;
myButtonView.wantsLayer = YES;
(Again, you can set this in the 'Layers' inspector of the XIB file, where you can click a checkbox next to each view to give it a layer) Now, the scroll view will only copy stuff from its own layer (which no longer includes your button). Also, now all the compositing of the text view will be done using in the graphics card. This works fine with an opaque push button, however, if you put text on a transparent background in its own layer (e.g. if you have a transparent pushbutton with a title), you lose sub-pixel anti-aliasing.

NSScrollView and ScrollToPoint on an NSImage

I have a NSView as the documentView for a NSScrollView. I also have a NSImageView as a subview of the NSView. The image dynamically changes size so the scroll bars become active/inactive at various times. Once the image has changed, I'd like to scroll to a certain point on the image. From within the NSView's drawRect: method, I call
[[myScrollView contentView] scrollToPoint: myPoint];
The scroll bars update and the image appears as I'd like, but as soon as the image is scrolled, a double image appears or parts of the image get cut off. Any help would be appreciated. Thank you.
Sounds like you might want to turn off the "Copy On Scroll" behavior option of the NSScrollView either in Interface Builder or programmatically.
From Scroll View Programming Guide for Mac OS X: How Scrolling Works:
The NSClipView class provides low-level scrolling support through the
scrollToPoint: method. This method translates the origin of the
content view’s bounds rectangle and optimizes redisplay by copying as
much of the rendered document view as remains visible, only asking the
document view to draw newly exposed regions. This usually improves
scrolling performance but may not always be appropriate. You can turn
this behavior off using the NSClipView method setCopiesOnScroll:
passing NO as the parameter. If you do leave copy-on-scroll active, be
sure to scroll the document view programmatically using the NSView
method scrollPoint: method rather than translateOriginToPoint:.

CALayer in NSCollectionViewItem flashes (and disappears) whenever a new item is added in the collection

My NSCollectionViewItem is in a separate xib file. It has an image view. Previously I was setting image directly on the NSImageView. The UI worked fine.
Now, I have added a CALayer to NSImageView. I use it to display images and transition from from one image to another.
Problem is: each time user adds a new NSCollectionViewItem. CALayers of NSCollectionViewItem blinks/flashes and sometimes disappear altogether. This happens at random. Sometimes CALayer of every item disappears. Other times only few items' CALayer disappear.
Then if I add another item or resize the NSCollectionView by resizing the container Window, which makes NSCollectionView to reshuffle all the items. CALayer appear back, but not always. This also random. Some items remain CALayer-less.
My guess is that animation of NSCollectionView is messing up the NSCollectionViewItem's CALayer.
Any idea what is going on? Is there some sample code/open source project that uses CALayer in NSCollectionViewItem?
Any help, any tip, no matter how small, would be highly appreciated.

Drawing an NSTableView's background outside of its bounds

I'm having a problem since Lion introduced elastic scrolling (pictured below). When you scroll my table view (cell-based, with alternating row colors) beyond its bounds, the background doesn't draw. I've tried doing my own drawing in -[drawBackgroundInClipRect:], but it seems like you can't exceed the bounds of the table view. How can I extend the background into elastic scrolling territory?
In Answer to Your Question
A view drawing outside its bounds is generally a no-no. When using alternating background colors, the NSTableView draws its background directly. But in a view-based table view, NSTableRowView is used and if it has its own background color, this is poses even more challenge.
The Bad News
The assemblage of NSScrollView (and its various parts), NSTableView, and NSTableHeaderView is complicated on its own. Once you throw view-based functionality into the mix (where each row has a view and each cell has their own view, and each are reused, animated around, etc.), overriding this behavior "is no way to go through life, son!" ;-)
The Good News
The issue of alternating background colors not extending in an elastically-stretched scrolled table view has been resolved (at least on 10.10, that I can tell), so this is no longer an issue unless you have row/cell views with custom backgrounds or just background colors.
A General Solution For Custom Document (Scrolled) Views
For all other scrolled views with ruled backgrounds you wish to extend for elastic scrolling, you'll need a custom NSScrollView subclass that expects a document view (your custom scrolled view) to conform to a protocol you define (like -(NSImage *)backgroundImageForClipViewBounds:). The scroll view would observe its content view (NSClipView) for bounds change notifications and flag itself for display. When the scroll view's -drawRect: is called, it would then ask the scrolled view for its -backgroundImageForClipViewBounds: (via your protocol) and draw it into itself, thereby making the scrolled view's "infinite background" its own.
I haven't tested this theory but I believe it would work. I hope this helps.

Resources