NSClipView ghosting, artifacts, trails on setDrawsBackground:NO - cocoa

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.

Related

Xcode - Visually identify custom views in interface builder / storyboard

If you build a custom UIView, and integrate it inside of a parent view/view controller in interface builder, the graphical element representing your custom view is invisible, if you don't specify a background color (I don't).
Is there any way, solely during development, to identify different custom views? Any hacks/tricks to distinguish them?
The closest I could come up with is setting the background color in IB, then removing the background in the implementation of the custom view.
Bounds Rectangles
You might find bounds rectangles useful. You can turn them on by going to the menu bar and choosing Editor > Canvas > Show Bounds Rectangles.
Here's an example. I have a view (a UICollectionViewCell subclass) laid out in a nib. It has a single-line label, a two-line label, and a custom subview. The custom subview itself contains a smaller custom subview. Here's the nib with bounds rectangles off:
Here's the same nib with bounds rectangles on:
Background Color Override
Here's another technique that builds on the idea of setting the background color. This technique requires your deployment target to be iOS 5.0 or later.
As you described, set the background color to make the view visible in the nib:
Then switch to the Identity Inspector and add backgroundColor in the User Defined Runtime Attributes section. Set it to the background color you want the view to have at runtime. For example, if you want it to be white at runtime:
If you want the background color to be clear, you can set backgroundColor to a color with opacity 0, or you can set it to “Nil” instead of any color:
That approach of setting the background color in Interface Builder, but resetting it in code is a simple, but effective technique. Two refinements:
If you have multiple custom views on a single storyboard scene, you can save yourself from having to programmatically clear the background color for all of them individually by using IBOutletCollection. So, in Interface Builder, give them all background colors and then add all of your custom views for a given scene to a collection. You then can set the background color for all of them in a single statement. So, for example, if you have a dozen controls on one scene all in a single IBOutletCollection is named viewsCollection:
#property (strong, nonatomic) IBOutletCollection(UIView) NSArray *viewsCollection;
you can clear the background color of all of them in a single statement:
[self.viewsCollection setValue:[UIColor clearColor] forKey:#"backgroundColor"];
You can also make the identification of your custom views in Interface Builder a little easier by setting the "Label" in the "Document" properties on the "Identity inspector":
Once you've done that, when you look at the document outline in the left side of the main panel, you'll see your labels show up:
Then, using the document outline makes it easier to identify your individual views in the scene. You can use a random label like I did here, or you could use the name of your custom view class, or whatever.

View is moved up when using vertical autoresize

In my custom drawn window I have a NSTextView under which I'd like to have NSScrollView separated by empty space. That's how I've set it up in xib.
In interface builder it looks fine, with nice space between the two of them
But when I run the program, the scroll view gets moves upwards, actually covering the text view:
But when I disable vertical autoresize of the scroll view, everything is working as it should.
text view and scroll view are under NSView so they are siblings to each other.
Most probably it was shortcoming of the system as the scroll view was being completely hidden and so then it was probably moved.
I've worked it out by subclassing NSScrollView and overriding
- (void)resizeWithOldSuperviewSize:(NSSize)oldSize_;
So now I can position the scroll view exactly as I wish.

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:.

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.

NSScrollView as subview of layer-backed view bug?

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.

Resources