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:.
Related
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.
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.
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.
When I make an NSScrollView a subview of a layer-backed view (for example by choosing an NSTextView or NSTableView from IB), I see strange drawing behavior in the scroll view's document view.
To illustrate, I created a simple project with an NSTextView in a window. The only code I wrote is to turn on layer-backing for the window's content view:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[[self.window contentView] setWantsLayer:YES];
}
This is the result when I type into the textview. The red underlines don't line up properly:
http://mowglii.com/random/screenshot.png
Also, the text and underlines jitter a lot when I resize the textview. I've seen the same jitter on resize when I use a tableview (inside a scrollview) instead of a textview.
Any idea what is going on?
NSScrollView indeed misbehaves badly when embedded in a layer-backed view.
Some serious trickery is needed to animate views containing scroll views. You could try turning on layer-backing only once you need to animate. You then need to force the drawing in order not to end up with an empty layer.
Usually, you will however need to go look much deeper in the back of tricks. Namely: Keep layer-backing switched off. Draw the view into an image, display that image in an overlay view with layer-backing enabled. Animate that view to an image of the final state. Then remove the overlay view to reveal the actual final state below.
I'm trying to get NSClipView to either draw a clear background (set the color to clear results in a black box), or no background at all. Drawing no background results in ghosting artifacts. Anyway to get this to draw no background without the artifacts???
The NSClipView is contained inside an NSBox subclass. An NSTextView is contained inside the NSClipView. It's basically an attempt at rolling my own NSTextField. I need to be able to draw a custom background and included subviews such as buttons.
The trails behavior is noted in the documentation of NSClipView. If the clip view is contained in a NSScrollView the documentation suggests calling setDrawsBackground: on the scroll view instead.
You could also try setting the background color of the scroll view to the clear color, assuming that the clip view is in a scroll view.
If the clip view is not in a scroll view then you might want to explain what the view hierarchy is like, and any code relavent to its construction.