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

11

我想自定义在OS X上绘制窗口标题栏的方式。具体来说,我想要做类似Twitterrific应用程序的事情,即使用自定义关闭按钮,没有最小/最大化按钮,并且窗口标题文本是右对齐的。与Twitterrific不同的是,我不想完全自定义绘制整个窗口(虽然我也没有完全排斥这种方式)。

我已经看过Cocoa With Love上的RoundWindow示例以及Apple提供的RoundTransparentWindow示例,但两者都不太合适。

3个回答

6
如果您不想使用无边框窗口类,那么您可以采取一些方法。
首先,您可以通过使用 -[NSWindow standardWindowButton:] 来自定义关闭/最小化/最大化按钮。一旦获取到按钮,您可以对其进行定位/删除等操作。
您可以将标题设置为 @"" 来自定义标题。然后,您可以添加一个 NSTextField 通过执行以下操作来绘制自己的标题: [[[NSWindow contentView] superview] addSubview:textField]
这可能是最简单的方法。
另一种方法是自定义绘制所有窗口标题栏等的视图。
NSWindow 的内容视图在“主题视图”中。您可以子类化主题视图并进行自己的绘制。唯一的问题是主题视图是一个私有类,所以您必须小心。

谢谢,Leibowitzn。你能提供一些有关如何设置NSTextField的更多细节吗?我该如何设置它的框架,以便在窗口中正确定位,并且在我的代码中最好的位置在哪里进行此操作? - sam
顺便说一下,我是在应用程序控制器的awakeFromNib方法中完成的。我将文本字段的框架相对于窗口的框架定位。这可能不是最好的方法,但我使用通知来知道窗口何时成为/放弃主窗口,并使用它来更改文本的颜色。再次感谢您的想法。 - sam

5

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

要点是创建一个NSWindow子类,并在init方法中将其styleMask设置为NSBorderlessWindowMask

- (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子类,用该类的实例填充整个窗口,然后从自定义视图中执行所有适当的窗口绘制。
整个过程可能有点痛苦。您将不得不重新实现大部分正常的窗口行为,例如通过拖动右下角进行调整大小。

谢谢,eJames。你说得对,这种方法(我之前已经开始尝试了)可能会很痛苦。我希望避免这种情况,因为我不需要完全自定义,也不想实现所有窗口的标准功能。这种方法基本上是在我问题中引用的示例中使用的方法。这种方法的一个奇怪之处是窗口的阴影似乎无法正常工作。它的颜色不像标准窗口上的阴影那么深,而且在焦点改变时也不会改变。我认为这可能是无边框窗口样式的一个错误。 - sam
影子问题很奇怪。听起来Leibowitzn的答案可能适合你。祝你好运! - e.James
阴影问题是一个长期存在的问题。苹果不会为没有边框且具有键盘焦点的窗口绘制更暗的阴影。例如,可以看看便笺应用。据苹果表示,解决此问题的推荐方法是使用普通窗口类型,然后进行自己的所有绘制工作。 - Leibowitzn

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接