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:
NSShadow
の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;
NSShadow *dropShadow = [[NSShadow alloc] init];
[dropShadow setShadowColor:[NSColor blackColor]];
[dropShadow setShadowBlurRadius:10.0];
[aView setShadow: dropShadow];
[super setContentView:aView];
}
@end
古いアプローチでは正確なラウンドコーナーを作成できないことに気づきました。そこで、例を更新して正確な丸い角を作りました。
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)
ここ の完全な実例。
少なくともOSX 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()
}
ここ から実用的な例をダウンロードできます。
Apple Developer Site にサンプルアプリケーションがあり、それが役立つかもしれません。このサンプルは、カスタムシェイプ、タイトルバーなし、透明なコンテンツを使用してウィンドウを作成する方法を示しています。また、ウィンドウの形状を変更し、ウィンドウの境界の周りのドロップシャドウを再計算する方法も示します。
アプリケーション用に独自のバージョンのXcodeスプラッシュ画面を作成しました。これには、最近のファイルリストビューが含まれ、四隅がすべて丸みを帯びています。
http://www.fizzypopstudios.com/splash.png
これを行うために私が見つけた最も簡単な方法は、InterfaceBuilderを使用してウィンドウを作成することでした。ウィンドウを800x470ピクセルにした後、「シャドウ」と「復元可能」を除くすべてのオプションをチェックを外しました。これにより、スプラッシュ画面を作成するための白紙の状態が残りました。
スプラッシュウィンドウのinitWithContentRect:styleMask:backing:defer:
メソッドで、次のプロパティも設定します。
self.opaque = NO
self.backgroundColor = [NSColor clearColor]
self.movableByWindowBackground = YES
この時点でウィンドウを表示した場合、背景はなく、ウィンドウの影はウィンドウ内の不透明なコントロールの背後に自動的に設定されます。
ウィンドウの背景を描くときは、左側の500ピクセルを薄い灰色で塗りつぶし、右側の残りの300ピクセルを白で塗りつぶします。表示された場合、この時点でウィンドウに丸い角はありません。
次に、[NSColor clearColor]
を使用して、ウィンドウの4つの角すべてを切り欠きます(正方形のサイズは、次に描画する丸い角の半径です)。次に、ベジェパスを使用して、切り取ったノッチに丸い角を描きます。
[NSColor clearColor]
で埋めると角が丸くなるベジェパスを作成してこれを実行しようとしましたが、他の色で塗りつぶされても、何らかの理由でパスがクリアで塗りつぶされませんでした。
これで、ウィンドウをレンダリングすると角が丸くなりますが、テーブルビューをウィンドウの右側にドロップすると、角は再び正方形になります。簡単な解決策は、背景を描画しないようにNSScrollView
を設定してから、ネストされたNSTableView
背景を透明に設定することです。
テーブルの後ろに背景を描画しているので、背景を描画するテーブルやスクロールビューは実際には必要ありません。これにより、丸みを帯びた角が保持されます。
四隅の近くにある他のコントロールがある場合は、丸みを帯びた領域に入らないように十分にインデントしてください。この例として、ウィンドウの左下にボタンがありますが、ボタンには透明度があるため、丸い角は削除されません。
もう1つの考慮事項は、ウィンドウサブクラスにcanBecomeKeyWindow
メソッドとcanBecomeMainWindow
メソッドを指定して、これらのタイプのウィンドウのデフォルトがYES
であるためNO
を返すことです。 。
この情報がウィンドウの作成に役立つことを願っています。しばらく前にあなたの質問を見て、戻ってきて、ウィンドウの作成方法をお知らせしたいと思いました。 :)
@ Eonil の answer のObjective Cの例:
[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];
Apple Developer Documentationが完全に指摘しているように:
レイヤーにシャドウを追加する場合、シャドウはレイヤーのコンテンツの一部ですが、実際にはレイヤーの境界の長方形の外側に広がります。その結果、レイヤーのmasksToBoundsプロパティを有効にすると、シャドウ効果がエッジの周りでクリップされます。レイヤーに透明なコンテンツが含まれている場合、レイヤーの真下の影の部分は表示されますが、レイヤーを超えて伸びる部分は表示されないという奇妙な効果が発生する可能性があります。シャドウが必要であると同時に境界マスキングも使用する場合は、1つではなく2つのレイヤーを使用します。コンテンツを含むレイヤーにマスクを適用してから、シャドウ効果が有効になっているまったく同じサイズの2番目のレイヤー内にそのレイヤーを埋め込みます。
したがって、実際には、2つのビュー/レイヤーを操作する必要があります。
- (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」の部分を詳しく見てください。オーストリア、チロルからの最高のご挨拶!
あなたは私が持っていたのと同じ問題に苦しんでいます、しかし良いニュースは私がそれを機能させるためにかなり簡単な方法を見つけたということです。
したがって、概念的には、Xcodeのウェルカム画面を見ると、通常のウィンドウのように見えますが、タイトルバーはありません(ありますか?) 。
私がやったことは、通常のウィンドウを取り、それを選択した状態で、Atribute Inspectorに移動し、タイトルバーの3つのボタンである[閉じる]、[最小化]、[サイズ変更]ボタンを無効にしました。 。
したがって、「裸の」ウィンドウが表示されますが、タイトルバーは表示されます。できることは、次のコードを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のウェルカム画面にかなり近く見えるはずです。必要に応じて、ホバー効果をプログラムすることもできます。これは非常に簡単です。
2つのオプションがあります。
コード:
@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
以前のプロジェクトでこれに遭遇しました。レイヤーに裏打ちされたビューは影を生成しません。ウィンドウのコンテンツビューを「通常の」(レイヤーに裏打ちされていない)ビューに設定すると、シャドウが表示されます。
これを試して:
- (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];
}
ビューの-drawRect:
で、描画後、[[self window] invalidateShadow];
を呼び出します。
InterfaceBuilderと2行の追加コードだけで実行できるソリューションは次のとおりです。
ウィンドウを含むNIBファイルをInterfaceBuilderで作成します。
次のように構成します。
Box(NSBox
)オブジェクトをInterface Builderのウィンドウコンテンツビューに追加し、コンテンツビュー全体に表示します。必要に応じて、ボックスの境界線のスタイル、境界線の太さ、境界線の色、塗りつぶしの色、およびコーナーの半径を設定します。
他のすべてのUIコンテンツをボックスに追加します。これは、ウィンドウコンテンツビューであるかのように扱います。
コードで、このNIBファイルをロードして所有者になるNSWindowController
のサブクラスを作成します。 -awakeFromNib
または-windowDidLoad
のいずれかに、次のコードを追加します。
self.window.opaque = NO;
self.window.backgroundColor = NSColor.clearColor;
それはすべての人々です。
レイヤー、カスタム描画コード、NSBezierPath
、NSShadow
はありません。
macOS10.9から10.15までのすべてのシステムで正しく動作することを確認しました