我正在尝试使用圆角和阴影来设置NSWindow
没有标题栏(NSBorderlessWindowMask
),类似于下面的“欢迎使用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
上的默认阴影消失了。如何将默认阴影添加到此窗口?
EDIT1:
NSWindow with 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
答案 0 :(得分:13)
我意识到旧方法无法创建精确的圆角。所以我更新了例子来制作精确的圆角。
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完整的工作示例。
至少在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()
}
您可以从here下载一个工作示例。
答案 1 :(得分:3)
Apple开发人员文档完美指出:
向图层添加阴影时,阴影是图层的一部分 内容但实际上扩展到图层的边界矩形之外。如 结果,如果为图层启用了masksToBounds属性,则 阴影效果被修剪边缘。如果您的图层包含任何 透明的内容,这可能会导致部分的奇怪效果 您图层正下方的阴影仍然可见,但部分 延伸到你的图层之外的不是。如果你想要一个阴影但也想要 要使用边界遮罩,可以使用两个图层而不是一个图层。适用 屏蔽包含内容的图层,然后嵌入该图层 在具有阴影的完全相同大小的第二层内 效果已启用。
所以实际上你必须使用两个视图/层:
- (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”部分。 来自奥地利蒂罗尔的最好的问候!
答案 2 :(得分:3)
我刚为我的应用程序创建了自己的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路径,我在一个圆角处画入我刚切出的凹槽。
我尝试通过创建一个bezier路径来实现这一点,当填充[NSColor clearColor]
时也会产生圆角,但是,由于某种原因,路径不会填充清晰,尽管它会填充其他颜色
现在,如果渲染窗口,则会有圆角,但是,如果将桌面视图放入窗口的右侧,角落将再次变为方形。简单的解决方案是将NSScrollView
设置为不绘制背景,然后将嵌套的NSTableView
背景设置为透明。
当我们绘制表格背后的背景时,我们并不需要绘制背景的表格或滚动视图,这样就可以保留圆角。
如果你有任何其他控件靠近4个角落,只需保持它们足够的缩进,不要在圆角区域内。这方面的一个例子是我的窗口左下角有一个按钮,但由于按钮具有透明度,因此不会删除圆角。
另一个考虑因素是您可能希望在窗口子类中提供canBecomeKeyWindow
和canBecomeMainWindow
方法以返回YES
,因为这些类型的窗口的默认值为NO
。
我希望这些信息可以帮助您创建窗口,我前一段时间看到了您的问题,并认为我会回来让您知道我是如何创建窗口的,以防它可以帮到您! :)
答案 3 :(得分:2)
Apple Developer Site上有一个示例应用程序可以帮助您。此示例演示如何使用自定义形状,无标题栏和透明内容创建窗口。它还显示了如何更改窗口的形状并重新计算窗口边框周围的阴影。
答案 4 :(得分:2)
[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];
答案 5 :(得分:1)
您有两种选择:
代码:
@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
答案 6 :(得分:1)
你正在努力解决我遇到的同样问题,但好消息是我找到了一种非常容易实现的方法。
所以,对于这些概念,如果你看看Xcode的欢迎屏幕,它几乎看起来像一个常规窗口,但没有标题栏(是吗?)。
我所做的是我已经选择了常规窗口并选择了它,我已经去了Atribute Inspector并停用了“关闭”,“最小化”和“调整大小”按钮,这三个按钮位于标题栏。
所以你得到一个“裸体”窗口,但仍然有标题栏。您可以做的是将以下代码添加到您的awakeFromNib委托:
[self.window setTitle:@""];
假设您的窗口在头文件中声明:
@property (assign) IBOutlet NSWindow *window;
现在你有一个完全裸的标题栏,你可以做的是最后的调整:
您也可以在awakeFromNib委托中执行此操作:
[self.window setBackgroundColor: [NSColor colorWithCalibratedWhite:0.97 alpha:1.0]];
我使用了以下方法: Adding a button or view to the NSWindow title bar
所以我可以使用(我也在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的欢迎屏幕,现在如果你愿意,你也可以设置悬停效果,这应该很容易。
答案 7 :(得分:1)
这是一个只需使用Interface Builder和两行额外的代码即可完成的解决方案:
在包含窗口的Interface Builder中创建NIB文件。
如下所示进行配置:
在Interface Builder中向窗口内容视图添加一个框(NSBox
)对象,并使其填充整个内容视图。根据需要设置框的边框样式,边框厚度,边框颜色,填充颜色和拐角半径:
将所有其他UI内容添加到框中,就像对待窗口内容视图一样。
在您的代码中,创建NSWindowController
的子类,该子类加载此NIB文件并因此成为所有者。在-awakeFromNib
或-windowDidLoad
中,添加以下代码:
self.window.opaque = NO;
self.window.backgroundColor = NSColor.clearColor;
这就是所有人。
没有图层,没有自定义工程图代码,没有NSBezierPath
,没有NSShadow
。
经过验证可以在macOS 10.9到10.15的所有系统上正常工作
答案 8 :(得分:0)
我在之前的项目中遇到过这个问题:图层支持的视图不会产生阴影。将窗口的内容视图设置为“常规”(非图层支持)视图,它将具有阴影。
答案 9 :(得分:0)
试试这个:
- (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 :(得分:0)
在您的视图-drawRect:
中,在绘图后,调用[[self window] invalidateShadow];
。