I'm trying to set custom background for an NSView. I need different backgrounds to be set based on some action. So i created 2 CAlayers for this view and trying to fill it using colorWithPatternImage.Is this a right method? If not, how can i do it?
Regards,
LS Developer
You could subclass the view that you want, and in its drawRect: method do something like
- (void)drawRect:(NSRect)dirtyRect
{
// Colour the background
[[NSColor orangeColor] set];
NSFillRect (dirtyRect);
// Now draw the parent
[super drawRect:dirtyRect];
}
Related
So I have created an NSWindow (with rounded corners), and in 10.10, it has a shadow around it. However when I tested in 10.9, the shadow disappeared. I have set breakpoints at every possible point, and [window hasShadow] is always YES.
If I set [self setOpaque:YES] in the initWithContentRect method of the window, the shadow comes back.
Has anybody seen this before? Or know what could possibly cause this?
It appears the hasShadow property doesn't do anything because if I set it to YES/NO it doesn't change anything. Just setting it opaque/transparent makes the shadow appear/disappear
Thanks in advance!
Here is how I finally managed this.
First of all, this will happen only if you are using layered back views (and this is our case if we want to easily implement rounded corners), in the RoundTransparentWindow Apple sample you can test it, until you not make the CutomView layered you will see the window shadow on 10.9 too, adding [self setWantsLayer:YES]; will kill your shadow.
The key for the solution here is adding all the layered views to a view that has no layer at all and making that view to the window contentView. That new contentView should reimplement only the drawRect: method on the following way:
- (void)drawRect:(NSRect)dirtyRect
{
[NSGraphicsContext saveGraphicsState];
[[NSColor clearColor] set];
NSRectFill(dirtyRect);
NSBezierPath *path = [NSBezierPath bezierPathWithRoundedRect:[self bounds]
xRadius:cCornerRadius
yRadius:cCornerRadius];
[[NSColor whiteColor] set];
[path fill];
[NSGraphicsContext restoreGraphicsState];
}
"Pre drawing" the rounded corners filled with any NONE transparent color is the key here, it will make the window server use the shadow again even if [self setOpaque:NO]; self.backgroundColor = [NSColor clearColor]; set during the window construction, that, as you found it earlier, made the server to think the window is transparent and does not need shadow.
Is it a chance to programatically change the background color of NSCollectionView?
I was trying subclassing.. but not working..
#interface CollectionViewBg : NSCollectionView
in .m
[self setBackgroundColors:[NSArray arrayWithObjects:[NSColor blueColor], nil]];
In .m, remove this line :
[self setBackgroundColors:[NSArray arrayWithObjects:[NSColor blueColor], nil]];
And use this code:
- (void)drawRect:(NSRect)dirtyRect{
[[NSColor blueColor] setFill];
NSRectFill(dirtyRect);
}
Also don't forget to change class of NSCollectionView object in IB to CollectionViewBg.
Hope this helps :)
Guess subclassing isn't a great idea, because most of Cocoa controls provide various techniques to avoid subclassing in order to customize appearance and behavior.
In this particular case, you can set backgroundView property to your NSCollectionView and provide a custom view that is dedicated for drawing a custom background.
If you need to change only background color, then you can consider take advantage of the fact that collection view usually resists inside scroll view. Just set backgroundColor and drawsBackground properties of NSScrollView.
Here's a more modern solution. First, make yourself an NSView subclass that draws the effect you want to achieve. Here's a simple one that paints a block colour:
class ColouredView: NSView {
#IBInspectable var color: NSColor = .windowBackgroundColor
override func draw(_ dirtyRect: NSRect) {
color.setFill()
dirtyRect.fill()
}
}
Now go to your nib or storyboard and drag a custom view onto the left bar. We don't want it to be part of the view hierarchy, but we do want it to be listed under the view controller containing the collection view. Just drag it in below 'First Responder'. You can then set up any important properties or relationships - in my case, the background colour.
Finally, right-click your collection view and connect its 'background view' outlet to your custom view. This lets you do all the setup in your storyboard. If you like you can make your custom view #IBDesignable but I find that usually causes more trouble than it's worth.
I have made a slick NSScroller subclass, but can't figure out how to make it overlay on top of the NSScrollView instead of pushing the documentView aside.
Here you can see the background of a NSCollectionView that I wish to make 100% wide, and have the scroller sit along top. Currently, I have to set a white background to the scroller because drawing with a clearColor is not showing as transparent, but as black.
Am I going about this the wrong way? Am I missing something obvious here? How can I achieve the behavior of a transparent-tracked NSScroller that sits atop a NSScrollView's contents?
I was able to get the positioning by implementing tile in the subclass OF NSSCROLLVIEW
- (void)tile {
[super tile];
[[self contentView] setFrame:[self bounds]];
}
And it turns out that trying to draw clear color was the problem to begin with. In the scroller subclass, I just omitted any drawing that had to do with the slider track completely BY OVERRIDING DRAWRECT: OF THE NSSCROLLER SUBCLASS, LIKE SO:
- (void)drawRect:(NSRect)dirtyRect
{
[self drawKnob];
}
Note that for this to work properly, you MUST enable layer-backing for the scrollView!
That is, call:
[scrollViewInstance setWantsLayer:YES];
or set it in Interface Builder.
If you don't do this, the scrollView's contentView will draw ON TOP OF the scrollers. Also: you should be aware that what you're doing is essentially overlapping two NSViews (NSScroller on top of NSScrollView --- both inherit from NSView.) Unlike UIViews on iOS, overlapping NSViews on OS X is not officially supported by any current version of the OS (10.6 down). Turning on CALayers seems to make it work, but it's still something to bear in mind. Of course, turning on layers can seriously kill drawing performance.
See this SO question for more detail: Is there a proper way to handle overlapping NSView siblings?
What's about this color: [NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:0.0]
I think you have to modify the sizes with the interface builder.
Whenever I try to create a custom window using NSBorderlessWindowMask and set an NSView (for example an NSImageView) as its contentView, I get a 1px gray border around the NSView and I don't seem to be able to get rid of it.
I have followed several approaches including Apple's RoundTransparentWindow sample code as well as several suggestions on StackOverflow.
I suspect the gray border is either coming from the window itself or the NSView.
Have any of you experienced this problem or do you have a possible solution?
The code is fairly straightforward. This is the init method of the custom window:
- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag {
self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
if (self != nil) {
[self setAlphaValue:1.0];
[self setBackgroundColor:[NSColor clearColor]];
[self setOpaque:NO];
}
return self;
}
To test this, in IB I place an NSImageView in that custom window WITHOUT border and yet the image in the NSImageView has a border. The same goes for other NSView subclasses, such as NSTextField, NSTableView.
In addition, I also noticed that the same is happening with the sample application (RoundTransparentWindow) of Apple. Is it even possible to draw an NSView in a custom window without a 1px border?
Thanks
Are you sure this happens when you use a regular NSView with no drawing? I bet not. Other controls (like NSImageView)have borders. Maybe you should double check to make sure they're turned off whe possible.
Update - How do you get your view into your window? You don't include that code. I created a basic test project (download it here) with an image well and it works just fine. See for yourself.
The problem
I have a transparent NSView on a transparent NSWindow. The view's drawRect: method draws some content (NSImages, NSBezierPaths and NSStrings) on the view but leaves parts of it transparent.
Clicking on the regions of the view that have been drawn on invokes the usual mouse event handling methods (mouseDown: and mouseUp:).
Clicking on the transparent areas gives focus to whatever window is behind my transparent window.
I would like to make parts of the transparent region clickable so that accidentally clicking between the elements drawn on my view does not cause the window to lose focus.
Solutions already attempted
Overriding the NSView's hitTest: method. Found that hitTest: was only called when clicking on a non-transparent area of the view.
Overriding the NSView's opaqueAncestor method. Found that this was not called when clicking on any part of the view.
Filling portions of the transparent area with [NSColor clearColor] in the drawRect: method, and with an almost-but-not-quite-transparent colour. This had no effect.
Experimented with the NSTrackingArea class. This appears to only add support for mouseEntered:, mouseExited:, mouseMoved:, and cursorUpdate: methods, not mouseUp: and mouseDown:.
I had the same problem. It looks like [window setIgnoresMouseEvents:NO] will do it.
(On Lion, at least. See http://www.cocoabuilder.com/archive/cocoa/306910-lion-breaks-the-ability-to-click-through-transparent-window-areas-when-the-window-is-resizable.html)
As far as I know, click events to transparent portions of windows aren't delivered to your application at all, so none of the normal event-chain overrides (i.e -hitTest:, -sendEvent:, etc) will work. The only way I can think of off the top of my head is to use Quartz Event Taps to capture all mouse clicks and then figure out if they're over a transparent area of your window manually. That, frankly, sounds like a huge PITA for not much gain.
George : you mentioned that you tried filling portions with an almost but not quite transparent color. In my testing, it only seems to work if the alpha value is above 0.05, so you might have some luck with something like this:
[[NSColor colorWithCalibratedRed:0.01 green:0.01 blue:0.01 alpha:0.05] set];
It's an ugly kludge, but it might work well enough to avoid using an event tap.
Did you try overriding
- (NSView *)hitTest:(NSPoint)aPoint
in your NSView sublcass?
You can use an event monitor to catch events outside a window/view.
https://developer.apple.com/library/mac/documentation/cocoa/conceptual/eventoverview/MonitoringEvents/MonitoringEvents.html
You can override the hitTest method in your NSView so that it always returns itself.
According to the NSView documentation, the hitTest method will either return the NSView that the user has clicked on, or nil if the point is not inside the NSView. In this case, I would invoke [super hitTest:], and then return the current view only if the result would otherwise be nil (just in case your custom view contains subviews).
- (NSView *)hitTest:(NSPoint)aPoint
{
NSView * clickedView = [super hitTest:aPoint];
if (clickedView == nil)
{
clickedView = self;
}
return clickedView;
}
You can do:
NSView* windowContent = [window contentView];
[windowContent setWantsLayer:YES]
Making sure that the background is transparent:
[[windowContent layer] setBackgroundColor:[[NSColor clearColor] CGColor]];
Another option would be to add a transparent background image that fills the contentView.