objective c - NSWindow con esquinas redondas y sombra.
objective-c cocoa (10)
Estoy tratando de NSWindow
un NSWindow
sin barra de título ( NSBorderlessWindowMask
) con esquinas redondeadas y una sombra, similar a la siguiente ventana "Bienvenido a Xcode".
Hago una subclase de 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
Y una subclase de NSView
:
@implementation ColoredView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
[[NSColor windowBackgroundColor] set];
NSRectFill(dirtyRect);
}
@end
Esto me da una ventana sin barra de título con esquinas redondeadas, pero la sombra predeterminada en NSWindow
se ha ido. ¿Cómo puedo agregar la sombra predeterminada a esta ventana?
EDIT1:
NSWindow con NSShadow
. Esta sombra no se muestra.
@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
Actualizar
Me di cuenta de que el enfoque anterior no era capaz de crear una esquina redonda precisa. Así que actualicé el ejemplo para hacer esquina redonda precisa.
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)
Here está el ejemplo de trabajo completo.
Oudado
No se requiere tratamiento especial al menos en 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://.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()
}
Puede descargar un ejemplo de trabajo desde here .
Acabo de crear mi propia versión de la pantalla de inicio de Xcode para mi aplicación, incluida la vista de lista de archivos recientes y las cuatro esquinas están redondeadas:
http://www.fizzypopstudios.com/splash.png
La forma más fácil que encontré para hacer esto fue crear una ventana usando el generador de interfaces. Hice la ventana de 800x470 píxeles, luego deseleccioné todas las opciones excepto "Sombra" y "Restaurable". Esto me dejó con una pizarra en blanco con la que crear mi pantalla de bienvenida.
En el initWithContentRect:styleMask:backing:defer:
para la ventana de initWithContentRect:styleMask:backing:defer:
también establezco las siguientes propiedades:
self.opaque = NO
self.backgroundColor = [NSColor clearColor]
self.movableByWindowBackground = YES
Si visualizase la ventana en este punto, no tendría fondo, y la sombra de la ventana se configuraría automáticamente para estar detrás de cualquier control no transparente en la ventana.
Cuando dibujo el fondo de la ventana, relleno los 500 píxeles de la izquierda con gris claro y los 300 píxeles restantes de la derecha con el blanco. Si se muestra la ventana no tendría esquinas redondeadas en este punto.
Luego uso [NSColor clearColor]
para hacer muescas en los cuadrados de las 4 esquinas de la ventana (el tamaño del cuadrado es el radio de la esquina redondeada que dibujaremos a continuación). Luego, utilizando Bezier Paths, dibujo una esquina redondeada hacia las muescas que acabo de cortar.
Intenté hacer esto creando una ruta más [NSColor clearColor]
que cuando se rellenara con [NSColor clearColor]
también daría como resultado una esquina redonda, sin embargo, por algún motivo, la ruta no se rellenaría con claridad, aunque se llenaría con otros colores.
Ahora, si renderiza la ventana tendrá esquinas redondeadas, sin embargo, si coloca una vista de tabla en el lado derecho de la ventana, las esquinas se volverán cuadradas nuevamente. La solución fácil es configurar NSScrollView
para que no dibuje un fondo, y luego configurar el fondo NSTableView
anidado en transparente.
Como estamos dibujando el fondo detrás de la mesa, realmente no necesitamos que la tabla o la vista de desplazamiento dibujen un fondo, y esto preserva las esquinas redondeadas.
Si tiene algún otro control que se acerque a las 4 esquinas, manténgalos lo suficientemente indentados para no estar en el área redondeada. Un ejemplo de esto es que tengo un botón en la parte inferior izquierda de mi ventana, sin embargo, como el botón tiene transparencia, la esquina redondeada no se elimina.
Otra consideración es que quizás desee proporcionar los métodos canBecomeKeyWindow
y canBecomeMainWindow
en su subclase de ventana para devolver YES
ya que el valor predeterminado para estos tipos de ventanas es NO
.
Espero que esta información te ayude a crear tu ventana. Hace un tiempo vi tu pregunta y pensé que volvería y te diría cómo creé mi ventana en caso de que pudiera ayudarte. :)
Como la Documentación de Desarrollador de Apple señala perfectamente:
Al agregar sombras a una capa, la sombra es parte del contenido de la capa, pero en realidad se extiende fuera del rectángulo de los límites de la capa. Como resultado, si habilita la propiedad masksToBounds para la capa, el efecto de sombra se recorta alrededor de los bordes. Si su capa contiene algún contenido transparente, esto puede causar un efecto extraño en el que la parte de la sombra directamente debajo de su capa aún es visible pero la parte que se extiende más allá de su capa no lo es. Si desea una sombra pero también desea utilizar el enmascaramiento de límites, use dos capas en lugar de una. Aplique la máscara a la capa que contiene su contenido y luego incruste esa capa dentro de una segunda capa del mismo tamaño que tiene el efecto de sombra habilitado.
Entonces, de hecho, tienes que trabajar con dos vistas / capas:
- (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];
}
Eche un vistazo más de cerca a la parte "initWithFrame" del código. Los mejores saludos desde el Tirol, Austria!
En su vista -drawRect:
después del dibujo, invoque [[self window] invalidateShadow];
.
Estás luchando con el mismo problema que tuve, pero la buena noticia es que he encontrado una manera bastante fácil de hacer que funcione.
Entonces, según los conceptos, si nos fijamos en la pantalla de bienvenida de Xcode, parece una ventana normal pero no tiene barra de título ( ¿verdad? ).
Configuración de la ventana
Lo que he hecho es tomar una ventana normal y, con ella seleccionada, he ido al Atribute Inspector y he desactivado los botones "Cerrar", "Minimizar" y "Cambiar tamaño", que son los tres botones en la barra de título. .
Así que obtienes una ventana "desnuda", pero aún tiene la barra de título. Lo que puede hacer es agregar el siguiente código a su delegado awakeFromNib:
[self.window setTitle:@""];
Suponiendo que su ventana se declara como en el archivo de encabezado:
@property (assign) IBOutlet NSWindow *window;
Ahora tienes una barra de título completamente desnuda y lo que puedes hacer es algunos ajustes finales:
Color
También puede hacer esto en el delegado awakeFromNib:
[self.window setBackgroundColor: [NSColor colorWithCalibratedWhite:0.97 alpha:1.0]];
Botón de cerrar
He utilizado el siguiente método: agregar un botón o una vista a la barra de título de NSWindow
Así que solo puedo usar (lo he hecho también en el delegado 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];
Ahora debería verse bastante cerca de la pantalla de bienvenida de xcode, ahora si lo desea, también puede programar los efectos de desplazamiento, que deberían ser bastante fáciles.
Hay una aplicación de ejemplo en el Sitio de desarrolladores de Apple que puede ayudarlo con eso. Este ejemplo muestra cómo crear ventanas con formas personalizadas, sin barra de título y contenido transparente. También muestra cómo cambiar la forma de la ventana y recalcular la sombra alrededor del borde de la ventana.
Me encontré con esto en un proyecto anterior: las vistas respaldadas por capas no producen una sombra. Establezca la vista de contenido de su ventana en una vista "normal" (sin respaldo de capa) y tendrá una sombra.
Objetivo C ejemplo de la de :
[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];
Tienes dos opciones:
- Usa las propiedades de sombra de la capa .
- Dibuja la sombra tú mismo en tu rutina drawRect. Para que esto funcione, no establezca las esquinas redondeadas, sino que utilice una ruta de inserción para dibujar el rectángulo redondeado y su sombra.
Código:
@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
prueba esto:
- (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];
}