I have a subview of an layer backed nsview (set through storyboard) which exceed the bounds. For some reason it is getting clipped. Any idea why this is happening ?
By default NSView will clip its subviews to its bounds.
If you have a layer backed NSView, alignmentRectInsets: might provide your solution. It returns an NSEdgeInsets that adds a margin to the layer's clipping bounds. Override the read-only property in your subview and return the desired insets.
If you need a more sophisticated way to inset the clipping, take a look at alignmentRectForFrame: and frameForAlignmentRect:.
See: developer.apple.com/…/Cocoa/Reference/ApplicationKit/Classes/NSView_Class
I just solved the problem.
It wasted me some time although the method is very simple.
There are two things need to pay attention to.
First of all (THE MOST IMPORTANT!!),you should set your winow's contentview.wantsLayer YES;
And then , set the parent view's layer.maskesToBounds NO;
What I did:
self.window.contentView.wantsLayer = YES;
self.testview.layer.backgroundColor = [NSColor clearColor].CGColor;
self.testview.layer.borderWidth = 3;
self.testview.layer.borderColor = [NSColor blueColor].CGColor;
self.testview.layer.masksToBounds = NO;
NSView *aView = [[NSView alloc]initWithFrame:NSMakeRect(-50, -20, 100, 100)];
aView.wantsLayer = YES;
aView.layer.backgroundColor = [NSColor redColor].CGColor;
[self.testview addSubview:aView];
The effect
GOOD LUCK
Related
If I resize the window to be smaller than the metal view I can see the scrollbars for a second but I cannot click on them nor they stay visible. Do you know how I can change this behavior? I would expect the scrollbars to be visible and clickable as long as the window is smaller than the metal view.
nsview = gdk_quartz_window_get_nsview(window);
NSScrollView *scroll_view = [[NSScrollView alloc]initWithFrame: [nsview frame]];
[scroll_view setBorderType: NSNoBorder];
[scroll_view setHasVerticalScroller: YES];
[scroll_view setHasHorizontalScroller: YES];
[scroll_view setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
[nsview addSubview: scroll_view];
self->clip_view = [[DvFlippedClipView alloc]initWithFrame: [nsview frame]];
[scroll_view setContentView: self->clip_view];
self->mtk_view = [[MTKView alloc]initWithFrame: [nsview frame]
device: self->device];
self->mtk_view.framebufferOnly = YES;
self->mtk_view.autoResizeDrawable = NO;
self->mtk_view.translatesAutoresizingMaskIntoConstraints = NO;
self->mtk_view_delegate = [[DvMetalViewDelegate alloc] init: self->mtk_view];
self->mtk_view.delegate = self->mtk_view_delegate;
[scroll_view setDocumentView: self->mtk_view];
From a different callback I do the following:
[self->mtk_view setBounds:NSMakeRect(0, 0, width, height)];
[self->mtk_view setFrame:NSMakeRect(0, 0, width, height)];
self->mtk_view.drawableSize = CGSizeMake(width, height);
I had a similar problem yesterday, and I suspect the strange scroller behavior and lack of scrolling altogether may be stemming from what I suspect your problem is. Hopefully it will at least enlighten your or someone else if you haven't already found an answer.
My view hierarchy looks like this:
The problem was that I had set MTKView as the view property of CenterTopViewController, rather than the Bordered Scroll View. Doing that, for all practical intents and purposes, removed MTKView from the hierarchy and set it so that its superview property pointed to the split view in which all of this resides. The scroll view did not seem to be part of the responder chain, and was never handling any scroll events (or at least not in any meaningful way).
Setting the scroll view as the view property of the view controller fixed everything.
P.S.
If you're confused as to why there is an unnecessary view above the MTKView, it's just a result of almost desperate attempts to uncover the problem before I figured it out. I haven't gotten around to fixing it yet.
I am looking into theming my app by setting window.appearance.
In my app, I draw some stuff inside layers. I also use Core Plot, which renders its charts in layers.
For the default aqua appearance, I just use the system colors (such as NSColor.textColor and NSColor.gridColor) and they are drawn in the correct color in CALayer. But changing the window's appearance to vibrant dark causes colors to be drawn incorrectly.
Is there any way to obtain the correct color for a givenNSAppearance? Private API is acceptable too.
If the question is not clear, here is a very simple example to show the problem.
I set up a CATextLayer that is added as a sublayer of the main view's sublayers and an NSTextFied that is added as a subview:
CATextLayer* textLayer = [CATextLayer new];
textLayer.string = #"Is this colored correctly? (Layer)";
textLayer.foregroundColor = NSColor.textColor.CGColor;
textLayer.contentsScale = 2.0;
textLayer.frame = (CGRect){0,0, [textLayer preferredFrameSize]};
NSTextField* textField = [NSTextField new];
textField.stringValue = #"Is this colored correctly? (View)";
textField.textColor = NSColor.textColor;
textField.font = (__bridge id)textLayer.font;
textField.editable = NO;
textField.selectable = NO;
textField.bezeled = NO;
textField.backgroundColor = nil;
[textField sizeToFit];
textField.frame = (CGRect){0, 60, textField.bounds.size};
[self.view.layer addSublayer:textLayer];
[self.view addSubview:textField];
On an Aqua window, both appear correctly:
However, on a dark vibrant window, the layer does not, while the text field does:
I'd like to know how to get the correct color for a given NSAppearance.
So I had an incorrect approach.
The right way to do it, is to implement -updateLayer in the view and take the colors' CGColor snapshot there. -updateLayer is called when the appearance changes, so the view can update it with the correct color values.
I have the following code:
[[ticketsListScrollView documentView] setFrame: NSMakeRect(0, 0, [ticketsListScrollView frame].size.width, 53 * [tickets count])];
[[ticketsListScrollView documentView] setFlipped:YES];
for(int i = 0; i < [tickets count]; i++) {
TicketsListViewController *viewController = [[TicketsListViewController alloc] initWithNibName:#"TicketsListViewController" bundle:nil];
viewController.dateLabelText = tickets[i][#"date"];
viewController.timeLabelText = tickets[i][#"time"];
viewController.subjectLabelText = tickets[i][#"title"];
NSRect frame = [[viewController view] frame];
frame.origin.y = frame.size.height * i;
[viewController view].frame = frame;
[[ticketsListScrollView documentView] addSubview:[viewController view]];
}
if the list is large enough (many views), the NSScrollView starts at top-left, which is great. For less views (the views do not take the whole documentView, then NSScrollView starts at the middle.
Any idea why?
Thank you!
Views are not flipped by default, which means your document view is being pinned to the lower-left corner (the default, non-flipped view origin) of the scroll view. What you're seeing is a view not tall enough to push the "top" subview to the top of the scroll view. I see you tried flipping this view, so you already know about this, but you're not doing it correctly.
I'm not sure why you're not getting an error or a warning when calling -setFlipped: since the isFlipped property is read-only. In your document view (the view that's scrolled, and in which you're placing all those subviews), you can override it:
- (BOOL)isFlipped {
return YES;
}
Of course you'll have to put this in a custom NSView subclass and set that as your scroll view's document view's class in IB if you're not creating it at runtime. You'll also need to adjust the frames you use for layout, since you're currently expressing them in the coordinate system of the scroll view's frame. You should be expressing them in your container/layout view's bounds coordinates, which will also be flipped, and so, likely different from your scroll view's coordinates. You'll also need to implement -intrinsicContentSize (and call -invalidateIntrinsicContentSize when adding/removing subviews) so auto-layout can size the container appropriately.
I have a NSView, subclassed, with custom background drawing, filling it with a gradient.
In IB, I've put a checkbox on it, somewhere in the middle.
This is the drawRect method.
-(void) drawRect:(NSRect)dirtyRect {
CGFloat sc = 0.9f;
CGFloat ec = 0.6f;
NSColor* startingColor = [NSColor colorWithDeviceRed:sc green:sc blue:sc alpha:1];
NSColor* endingColor = [NSColor colorWithDeviceRed:ec green:ec blue:ec alpha:1];
NSGradient *grad = [[NSGradient alloc] initWithStartingColor:startingColor endingColor:endingColor];
[grad drawInRect:dirtyRect angle:270];
}
What happens is, this same method gets called to draw the whole view area first and then for the part, where NSButton (checkbox) lies on top of it. OF course the checkbox background is drawn with a complete gradient and it is not right, since the portion is much smaller. The same happens with other controls I put on the said NSView.
What is the suggested approach on such thing?
One option is to make controls height the same as the views' but this will result in problems in the future.
The answer is, always draw the WHOLE area of the view, not just the dirtyRect
[grad drawInRect:[self bounds] angle:270];
I am doing manual layouting for my Cocoa application and at some point I need to figure out what the inner size of a NSView subclass is. (E.g. What is the height available for my child view inside of a NSBox?)
One of the reasons is that I am using a coordinate system with origin at the top-left and need to perform coordinate transformations.
I could not figure out a way to get this size so far and would be glad if somebody can give me a hint.
Another very interesting property I would like to know is the minimum size of a view.
-bounds is the one you're looking for in most views. NSBox is a bit of a special case, however, since you want to look at the bounds of the box's content view, not the bounds of the box view itself (the box view includes the title, edges, etc.). Also, the bounds rect is always the real size of the box, while the frame rect can be modified relative to the bounds to apply transformations to the view's contents (such as squashing a 200x200 image into a 200x100 frame).
So, for most views you just use [parentView bounds], and for NSBox you'll use [[theBox contentView] bounds], and you'll use [[theBox contentView] addSubview: myView] rather than [parentView addSubview: myView] to add your content.
Unfortunately, there is no standard way to do this for all NSView subclasses. In your specific example, the position and size of a child view within an NSBox can be computed as follows:
NSRect availableRect = [someNSBox bounds];
NSSize boxMargins = [someBox contentViewMargins];
availableRect = NSInsetRect(availableRect, boxMargins.width, boxMargins.height);
If you find yourself using this often, you could create a category on NSBox as follows:
// MyNSBoxCategories.h
#interface NSBox (MyCategories)
- (NSRect)contentFrame;
#end
// MyNSBoxCategories.m
#implementation NSBox (MyCategories)
- (NSRect)contentFrame
{
NSRect frameRect = [self bounds];
NSSize margins = [self contentViewMargins];
return NSInsetRect(frameRect, margins.width, margins.height);
}
#end
And you would use it like so:
#import "MyNSBoxCategories.h"
//...
NSRect frameRect = [someNSBox contentFrame];
[myContentView setFrame:frameRect];
[someNSBox addSubview:myContentView];
The bounds property of NSView returns an NSRect with the origin (usually (0,0)) and the size of an NSView. See this Apple Developer documentation page.
I'm not sure (I never had to go too deep in that stuff), but isn't it [NSView bounds]?
http://www.cocoadev.com/index.pl?DifferenceBetweenFrameAndBounds