NSWindow with round corners and shadow

后端 未结 11 1830
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-12-23 02:26

I\'m trying to crate a NSWindow without title bar (NSBorderlessWindowMask) with round corners and a shadow, similar to the below \"Welcome

相关标签:
11条回答
  • 2020-12-23 02:46

    Here's a solution that can be done just with Interface Builder and two lines of extra code:

    Create a NIB file in Interface Builder containing your window.
    Configure it as shown below:

    Add a box (NSBox) object to the window content view in Interface Builder and make it fill the entire content view. Set border style, border thickness, border color, fill color and corner radius of the box as desired:

    Add all your other UI content to the box, treat is as if it was your window content view.

    In your code, create a subclass of NSWindowController which loads this NIB file and thus becomes the owner. Either in -awakeFromNib or -windowDidLoad, add the following code:

        self.window.opaque = NO;
        self.window.backgroundColor = NSColor.clearColor;
    

    That's all folks.
    No layers, no custom drawing code, no NSBezierPath, no NSShadow.
    Verified to work correctly on all systems from macOS 10.9 to 10.15

    0 讨论(0)
  • 2020-12-23 02:47

    As the Apple Developer Documentation perfectly points out:

    When adding shadows to a layer, the shadow is part of the layer’s content but actually extends outside the layer’s bounds rectangle. As a result, if you enable the masksToBounds property for the layer, the shadow effect is clipped around the edges. If your layer contains any transparent content, this can cause an odd effect where the portion of the shadow directly under your layer is still visible but the part extending beyond your layer is not. If you want a shadow but also want to use bounds masking, you use two layers instead of one. Apply the mask to the layer containing your content and then embed that layer inside a second layer of the exact same size that has the shadow effect enabled.

    Full Article: https://developer.apple.com/library/mac/documentation/cocoa/conceptual/coreanimation_guide/SettingUpLayerObjects/SettingUpLayerObjects.html#//apple_ref/doc/uid/TP40004514-CH13-SW12

    So in fact you have to work with two views/layers:

    - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)bufferingType defer:(BOOL)flag
    {
    self = [super initWithContentRect:contentRect styleMask:NSBorderlessWindowMask backing:bufferingType defer:flag];
    
    if ( self )
    {
        [self setOpaque:NO];
        [self setBackgroundColor:[NSColor clearColor]];
        [self setMovableByWindowBackground:TRUE];
        [self setStyleMask:NSBorderlessWindowMask];
        [self setHasShadow:YES];
    }
    
    return self;
    }
    
    - (BOOL)canBecomeKeyWindow {
    return YES;
    }
    
    -(BOOL)canBecomeMainWindow {
    return YES;
    }
    
    - (void) setContentView:(NSView *)aView {
    
    NSView *backView = [[NSView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    backView.wantsLayer             = YES;
    backView.layer.masksToBounds    = NO;
    backView.layer.shadowColor      = [NSColor shadowColor].CGColor;
    backView.layer.shadowOpacity    = 0.5;
    backView.layer.shadowOffset     = CGSizeMake(0, -3);
    backView.layer.shadowRadius     = 5.0;
    backView.layer.shouldRasterize  = YES;
    
    
    NSView *frontView = [aView initWithFrame:CGRectMake(backView.frame.origin.x + 15, backView.frame.origin.y + 15, backView.frame.size.width - 30, backView.frame.size.height - 30)];
    [backView addSubview: frontView];
    frontView.layer.cornerRadius    = 8;
    frontView.layer.masksToBounds   = YES;
    frontView.layer.borderColor     = [[NSColor darkGrayColor] CGColor];
    frontView.layer.borderWidth     = 0.5;
    
    [super setContentView:backView];
    
    }
    

    Have a closer look at the "initWithFrame" part of the code. Best greetings from Tyrol, Austria!

    0 讨论(0)
  • 2020-12-23 02:50

    try this:

    - (id)initWithContentRect:(NSRect)contentRect
                    styleMask:(NSUInteger)windowStyle   
                      backing:(NSBackingStoreType)bufferingType
                        defer:(BOOL)deferCreation
    {
        self = [super
                initWithContentRect:contentRect
                styleMask:NSBorderlessWindowMask | NSResizableWindowMask
                backing:bufferingType
                defer:deferCreation];
        if (self)
        {
            [self setOpaque:NO];
           [self setBackgroundColor:[NSColor clearColor]];            
    
        }
    
          [self setHasShadow:YES];
    
    
        [self setMovableByWindowBackground:YES];
        [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(didBecomeKey:) name:NSWindowDidBecomeKeyNotification object:self];
    
        return self;
    }
    
    -(void)didBecomeKey:(NSNotification *)notify{
        [self performSelector:@selector(invalidateShadow) withObject:nil afterDelay:.1];
    }
    
    0 讨论(0)
  • 2020-12-23 02:54

    I ran across this in a previous project: layer-backed views do not produce a shadow. Set your window's content view to a "regular" (non-layer-backed) view and it will have a shadow.

    0 讨论(0)
  • 2020-12-23 02:55

    In your view's -drawRect:, after the drawing, invoke [[self window] invalidateShadow];.

    0 讨论(0)
  • 2020-12-23 03:01

    You are struggling with the same issue I had, but the good news is that I have found a way pretty much easy to make it work.

    So, to the concepts, if you look at Xcode's welcome screen, it pretty much looks like a regular window but has no title bar (does it?).

    Window Setup

    What I've done is I've taken a regular window and with it selected, I've gone to the Atribute Inspector and deactivated the "Close", "Minimize" and "Resize" buttons which are the three buttons at the title bar.

    So you get a "naked" window, but still has the title bar. What you can do is to add the following code to your awakeFromNib delegate:

    [self.window setTitle:@""];
    

    Assuming your window is declared like in the header file:

    @property (assign) IBOutlet NSWindow *window;
    

    Now you have a completely naked title bar and what you can do is some final tweaking:

    Color

    You can do this in the awakeFromNib delegate as well:

    [self.window setBackgroundColor: [NSColor colorWithCalibratedWhite:0.97 alpha:1.0]];
    

    Close Button

    I've used the following method: Adding a button or view to the NSWindow title bar

    So I can just use (I've done that also in the awakeFromNib delegate):

    //Creating the button:
    NSButton *closeButton = [[NSButton alloc] initWithFrame:NSMakeRect(0,0,12,12)];
    NSButtonCell *closeButtonCell = [closeButton cell];
    
    [closeButton setBezelStyle:NSCircularBezelStyle];
    [closeButton setTitle:@""];
    [closeButton setBordered:NO];    
    [closeButton setImage:[NSImage imageNamed:NSImageNameStopProgressFreestandingTemplate]];
    [closeButtonCell setImageScaling:NSImageScaleProportionallyDown];
    [closeButtonCell setBackgroundColor:[NSColor clearColor]];
    [closeButton setAlphaValue:0.5];
    
    //Calling the button:
    [self.window addViewToTitleBar:closeButton atXPosition:8];
    

    Now it should look pretty much close to the xcode's welcome screen, now if you want, you can also program the hover effects, which should be pretty easy.

    0 讨论(0)
提交回复
热议问题