Подтвердить что ты не робот

NSWindow с круглыми углами и тенью

Я пытаюсь создать NSWindow без заголовка (NSBorderlessWindowMask) с круглыми углами и тень, как показано ниже в окне "Добро пожаловать в Xcode".

Welcome to Xcode

Я делаю подкласс NSWindow:

@implementation FlatWindow

- (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;
}

- (void) setContentView:(NSView *)aView
{
    aView.wantsLayer            = YES;
    aView.layer.frame           = aView.frame;
    aView.layer.cornerRadius    = 10.0;
    aView.layer.masksToBounds   = YES;

    [super setContentView:aView];
}

@end

И подкласс NSView:

@implementation ColoredView

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    [[NSColor windowBackgroundColor] set];
    NSRectFill(dirtyRect);
}

@end

Это дает мне окно без заголовка с круглыми углами, но тень по умолчанию на NSWindow отсутствует. Как добавить тень по умолчанию в это окно?

Flat window

EDIT1:

NSWindow с NSShadow. Эта тень не отображается.

@implementation FlatWindow

- (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;
}

- (void) setContentView:(NSView *)aView
{
    aView.wantsLayer            = YES;
    aView.layer.frame           = aView.frame;
    aView.layer.cornerRadius    = 10.0;
    aView.layer.masksToBounds   = YES;

    NSShadow *dropShadow = [[NSShadow alloc] init];
    [dropShadow setShadowColor:[NSColor blackColor]];
    [dropShadow setShadowBlurRadius:10.0];
    [aView setShadow: dropShadow];

    [super setContentView:aView];
}

@end
4b9b3361

Ответ 1

Update

Я понял, что старый подход не смог создать точный круглый угол. Поэтому я обновил пример, чтобы сделать точный круглый угол.

введите описание изображения здесь

        window1.backgroundColor             =   NSColor.whiteColor()
        window1.opaque                      =   false
        window1.styleMask                   =   NSResizableWindowMask
                                            |   NSTitledWindowMask
                                            |   NSFullSizeContentViewWindowMask
        window1.movableByWindowBackground   =   true
        window1.titlebarAppearsTransparent  =   true
        window1.titleVisibility             =   .Hidden
        window1.showsToolbarButton          =   false
        window1.standardWindowButton(NSWindowButton.FullScreenButton)?.hidden   =   true
        window1.standardWindowButton(NSWindowButton.MiniaturizeButton)?.hidden  =   true
        window1.standardWindowButton(NSWindowButton.CloseButton)?.hidden        =   true
        window1.standardWindowButton(NSWindowButton.ZoomButton)?.hidden         =   true

        window1.setFrame(CGRect(x: 400, y: 0, width: 400, height: 500), display: true)
        window1.makeKeyAndOrderFront(self)

Здесь полный рабочий пример.


Oudated

Специальная обработка не требуется, по крайней мере, в OS X 10.10.

import Cocoa

class ExampleApplicationController: NSObject, NSApplicationDelegate {
    class ExampleController {

        let window1 =   NSWindow()
        let view1   =   NSView()

        init(){
            window1.setFrame(CGRect(x: 400, y: 0, width: 400, height: 500), display: true)
            window1.contentView                 =   view1

            window1.backgroundColor             =   NSColor.clearColor()
            window1.opaque                      =   false
            window1.styleMask                   =   NSBorderlessWindowMask | NSResizableWindowMask
            window1.movableByWindowBackground   =   true
            window1.makeKeyAndOrderFront(self)

            view1.wantsLayer                =   true
            view1.layer!.cornerRadius       =   10
            view1.layer!.backgroundColor    =   NSColor.whiteColor().CGColor

            /// :ref:   http://stackoverflow.com/questions/19940019/nswindow-with-round-corners-and-shadow/27613308#21247949
            window1.invalidateShadow()  //  This manual invalidation is REQUIRED because shadow generation is an expensive operation.
        }
    }

    let example1    =   ExampleController()
}

Вы можете загрузить рабочий пример из здесь.

Ответ 2

Как прекрасно отмечает Apple Developer Documentation:

При добавлении теней к слою тень является частью слоев но фактически выходит за пределы границ границ границ границ. В виде результат, если вы включите свойство masksToBounds для этого слоя, теневой эффект обрезается по краям. Если ваш слой содержит прозрачный контент, это может вызвать нечетный эффект, когда часть тень прямо под вашим слоем все еще видна, но часть простираясь за пределы вашего слоя, нет. Если вы хотите тень, но также хотите для использования маскирования границ вы используете два слоя вместо одного. Примените маскировать слой, содержащий ваш контент, а затем вставлять этот слой внутри второго слоя того же размера, который имеет тень эффект включен.

Полная статья: https://developer.apple.com/library/mac/documentation/cocoa/conceptual/coreanimation_guide/SettingUpLayerObjects/SettingUpLayerObjects.html#//apple_ref/doc/uid/TP40004514-CH13-SW12

Поэтому на самом деле вам нужно работать с двумя видами/слоями:

- (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];

}

Познакомьтесь с частью кода "initWithFrame". Лучшие приветствия из Тироля, Австрия!

Ответ 3

На сайте разработчика Apple есть пример приложения, которое может вам помочь. В этом примере показано, как создавать окна с настраиваемыми формами, без заголовка и прозрачного содержимого. Он также показывает, как изменить форму окна и пересчитать тень вокруг границы окна.

Ответ 4

Я только что создал свою собственную версию заставки Xcode для моего приложения, включая просмотр списка последних файлов и все четыре угла округлены:

http://www.fizzypopstudios.com/splash.png

Самый простой способ найти это - создать окно с помощью построителя интерфейса. Я сделал окно 800x470 пикселей, затем я не отмечен все параметры, кроме "Тень" и "Восстановимость". Это оставило меня с пустым списком, с помощью которого можно создать свой заставку.

В методе initWithContentRect:styleMask:backing:defer: для окна всплеска я также устанавливаю следующие свойства:

self.opaque = NO
self.backgroundColor = [NSColor clearColor]
self.movableByWindowBackground = YES

Если вы отобразили окно в этот момент, у вас не было фона, и тень окна автоматически будет установлена ​​за любыми непрозрачными элементами управления в окне.

Когда я рисую фон моего окна, я заполняю левые 500 пикселей светло-серыми, а остальные 300 пикселей справа - белыми. Если на дисплее отобразится окно, в этой точке не будет закругленных углов.

Затем я использую [NSColor clearColor], чтобы вырезать квадраты из всех 4 углов окна (размер квадрата - это радиус закругленного угла, который мы будем рисовать дальше). Затем, используя Bezier Paths, я рисую округленный угол в вырезы, которые я только что вырезал.

Я попытался сделать это, создав путь безье, который при заполнении с помощью [NSColor clearColor] также приведет к круговому углу, однако по какой-то причине путь не будет заполняться ясным, хотя он будет заполняться другими цветами.

Теперь, если вы создадите окно, у вас будут закругленные углы, однако, если вы отбросите табличный вид в правой части окна, углы снова станут квадратными. Легкое решение состоит в том, чтобы установить NSScrollView не нарисовать фон, а затем установить прозрачный фон вложенных NSTableView.

Как мы рисуем фон за столом, нам не нужна таблица или прокрутка, рисующая фон, а затем сохраняются закругленные углы.

Если у вас есть другие элементы управления, которые приближаются к 4 углам, просто держите их достаточно отступом, чтобы их не было в закругленной области. Например, у меня есть кнопка в левом нижнем углу окна, однако, поскольку кнопка имеет прозрачность, округленный угол не удаляется.

Еще одно соображение: вы можете предоставить методы canBecomeKeyWindow и canBecomeMainWindow в вашем подклассе окна, чтобы вернуть YES, поскольку по умолчанию для этих типов окон есть NO.

Я надеюсь, что эта информация поможет вам создать свое окно, я давно видел ваш вопрос и подумал, что вернусь и дам вам знать, как я создал свое окно, если это может вам помочь!:)

Ответ 5

Цель C примера @Eonil answer:

[window setBackgroundColor:[NSColor whiteColor]];
[window setOpaque:NO];
[window setStyleMask:NSResizableWindowMask | NSTitledWindowMask | NSFullSizeContentViewWindowMask];
[window setMovableByWindowBackground:YES];
[window setTitlebarAppearsTransparent:YES];
[window setTitleVisibility:NSWindowTitleHidden];
[window setShowsToolbarButton:NO];
[window standardWindowButton:NSWindowFullScreenButton].hidden = YES;
[window standardWindowButton:NSWindowMiniaturizeButton].hidden = YES;
[window standardWindowButton:NSWindowCloseButton].hidden = YES;
[window standardWindowButton:NSWindowZoomButton].hidden = YES;
[window makeKeyWindow];

Ответ 6

У вас есть два варианта:

  • Используйте свойства тени слоя.
  • Нарисуйте тень самостоятельно в своей рутине drawRect. Для этого не устанавливайте закругленные углы, а используйте путь вставки, чтобы нарисовать округленный прямоугольник и его тень.

код:

@interface RoundedOuterShadowView : NSView {
}

@end

@implementation RoundedOuterShadowView

- (id)initWithFrame: (NSRect)frameRect
{
    self = [super initWithFrame: frameRect];
    if (self != nil) {
    }

    return self;
}

// Shared objects.
static NSShadow *borderShadow = nil;

- (void)drawRect: (NSRect)rect
{
    [NSGraphicsContext saveGraphicsState];

    // Initialize shared objects.
    if (borderShadow == nil) {
        borderShadow = [[NSShadow alloc] initWithColor: [NSColor colorWithDeviceWhite: 0 alpha: 0.5]
                                                offset: NSMakeSize(1, -1)
                                            blurRadius: 5.0];
    }

    // Outer bounds with shadow.
    NSRect bounds = [self bounds];
    bounds.size.width -= 20;
    bounds.size.height -= 20;
    bounds.origin.x += 10;
    bounds.origin.y += 10;

    NSBezierPath *borderPath = [NSBezierPath bezierPathWithRoundedRect: bounds xRadius: 5 yRadius: 5];
    [borderShadow set];
    [[NSColor whiteColor] set];
    [borderPath fill];

    [NSGraphicsContext restoreGraphicsState];
}

@end

Ответ 7

Вы боретесь с той же проблемой, что и я, но хорошей новостью является то, что я нашел способ, который очень легко заставить его работать.

Итак, к концепциям, если вы посмотрите экран приветствия Xcode, он в значительной степени похож на обычное окно, но не имеет заголовка (делает это?).

Настройка окна

Что я сделал, так это то, что я взял обычное окно и выбрал его, я пошел в Inspectute Inspector и отключил кнопки "Закрыть", "Свернуть" и "Изменить размер", которые являются тремя кнопками на строка заголовка.

Итак, вы получаете "голые" окна, но все же имеете заголовок. Что вы можете сделать, так это добавить следующий код в ваш делегат awakeFromNib:

[self.window setTitle:@""];

Предполагая, что ваше окно объявлено как в файле заголовка:

@property (assign) IBOutlet NSWindow *window;

Теперь у вас есть полностью голая строка заголовка, и что вы можете сделать, это окончательная настройка:

Цвет

Вы можете сделать это и в делегате awakeFromNib:

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

Кнопка закрытия

Я использовал следующий метод: Добавление кнопки или просмотра в строку заголовка NSWindow

Поэтому я могу просто использовать (я сделал это и в делегате awakeFromNib):

//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];

Теперь он должен выглядеть почти вплотную к окну приветствия xcode, теперь, если вы хотите, вы также можете запрограммировать эффекты наведения, которые должны быть довольно легкими.

Ответ 8

Я просмотрел это в предыдущем проекте: просмотры с поддержкой слоев не создают тени. Настройте представление содержимого окна на "обычный" (без слоя) вид и у него будет тень.

Ответ 9

попробуйте следующее:

- (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];
}

Ответ 10

В вашем представлении -drawRect: после рисования вызовите [[self window] invalidateShadow];.