问题 如何在Objective-C中自定义绘制窗口标题栏?


我想自定义我在OS X上绘制窗口标题栏的方式。具体来说,我想做一些像Twitterrific应用程序,其中有自定义关闭按钮,没有最小/最大按钮,窗口标题文本是对的。与Twitterrific不同,我不打算自定义绘制整个窗口(尽管我并不完全反对)。

我已经在Cocoa With Love以及Apple提供的RoundTransparentWindow示例中看到了RoundWindow示例,但似乎都不合适。


7790
2017-11-03 03:55


起源



答案:


如果您不想使用无边界窗口类,那么您可以做一些事情。

首先,您可以使用自定义关闭/最小/最大按钮购买 -[NSWindow standardWindowButton:]。一旦你得到按钮,你可以定位/删除/等...

您可以通过将标题设置为自定义标题 @""。然后你可以添加一个 NSTextField 通过执行以下操作来绘制自己的标题 [[[NSWindow contentView] superview] addSubview:textField]

这可能是最简单的做事方式。

另一种方法是自定义绘制所有窗口标题栏的视图等...

NSWindow的内容视图位于“主题视图”中。您可以子类化主题视图并执行自己的绘图。唯一的问题是主题视图是一个私人类,所以你必须要小心。


6
2017-11-03 05:31



谢谢Leibowitzn。您能否提供一些有关如何设置NSTextField的更多详细信息?如何设置它的框架,以便它在窗口中正确定位,并在我的代码中最适合这样做的位置? - sam
FWIW,我是在app控制器的awakeFromNib方法中完成的。我将文本框架的框架相对于窗口框架定位。这可能不是最好的方法,但是我使用通知知道窗口何时成为/重新占用主窗口并用它来改变文本的颜色。再次感谢您的想法。 - sam


答案:


如果您不想使用无边界窗口类,那么您可以做一些事情。

首先,您可以使用自定义关闭/最小/最大按钮购买 -[NSWindow standardWindowButton:]。一旦你得到按钮,你可以定位/删除/等...

您可以通过将标题设置为自定义标题 @""。然后你可以添加一个 NSTextField 通过执行以下操作来绘制自己的标题 [[[NSWindow contentView] superview] addSubview:textField]

这可能是最简单的做事方式。

另一种方法是自定义绘制所有窗口标题栏的视图等...

NSWindow的内容视图位于“主题视图”中。您可以子类化主题视图并执行自己的绘图。唯一的问题是主题视图是一个私人类,所以你必须要小心。


6
2017-11-03 05:31



谢谢Leibowitzn。您能否提供一些有关如何设置NSTextField的更多详细信息?如何设置它的框架,以便它在窗口中正确定位,并在我的代码中最适合这样做的位置? - sam
FWIW,我是在app控制器的awakeFromNib方法中完成的。我将文本框架的框架相对于窗口框架定位。这可能不是最好的方法,但是我使用通知知道窗口何时成为/重新占用主窗口并用它来改变文本的颜色。再次感谢您的想法。 - sam


cocoadev 提供有关如何最好地实现自己的更多细节 NSWindow 子类,包含对大多数常见陷阱的描述。

它的要点是创建一个子类 NSWindow,并设置它 styleMask 至 NSBorderlessWindowMask 在init方法中:

- (id) initWithContentRect: (NSRect) contentRect
                 styleMask: (unsigned int) aStyle
                   backing: (NSBackingStoreType) bufferingType
                     defer: (BOOL) flag
{
    if ((self = [super initWithContentRect: contentRect
                                 styleMask: NSBorderlessWindowMask
                                   backing: bufferingType
                                     defer: flag]) == nil) { return nil; }

    [super setMovableByWindowBackground:YES];
    [super setLevel:NSNormalWindowLevel];
    [super setHasShadow:YES];
    // etc.

    return self;
}

请注意,您应该返回YES canbecomeKeyWindow 为了使您的窗口像普通窗口一样。

- (BOOL) canBecomeKeyWindow
{
    return YES;
}

然后,您可以创建自定义NSView子类,使用所述类的实例填充整个窗口,然后在该自定义视图中执行所有适当的窗口绘制。

整件事情可能会有点痛苦。您将不得不重新实现大多数常规窗口行为,例如通过拖动右下角来调整大小。


5
2017-11-03 04:24



谢谢,eJames。你是对的,这种方法(我之前开始的)可能会变得痛苦。我希望避免它,因为我不是在寻找完全自定义,我不想实现所有窗口的标准功能。这种方法基本上是我在我的问题中引用的样本中使用的方法。这种方法的一个奇怪的方面是窗口的阴影似乎无法正常工作。它不像标准窗口上的阴影那么暗,并且在焦点改变时不会改变。我认为这可能是无边框窗口样式的错误。 - sam
阴影问题很奇怪。这听起来像Leibowitzn的答案可能是你的方式。祝你好运! - e.James
影子问题是一个长期存在的问题。 Apple没有为具有键盘焦点的无边框窗口绘制更暗的阴影。以Stickies为例。根据Apple的说法,推荐的解决方法是使用普通的窗口类型然后自己绘制。 - Leibowitzn


CoreData Stickies示例项目中有一个自定义窗口实现的示例。


2
2017-11-03 08:14