【问题标题】:Changing color of NSWindow title text更改 NSWindow 标题文本的颜色
【发布时间】:2012-09-07 17:25:32
【问题描述】:

我知道这不会是一个受欢迎的问题,而且有些人不喜欢外观不标准的应用,但它对我的应用很有用。

是否可以以“标准”非私有 API 方式更改 NSWindow 标题栏文本的颜色?

我知道如果我使用私有 API (as mentioned in this answer)) 是可能的,但我认为没有私有 API 也可以做到这一点,因为 Pixelmator 已经做到了,并且没有被 MAS 拒绝。我知道也可以通过使一个无边框的窗口并自己绘制所有内容,但我不认为 Pixelmator 是这样做的,因为他们仍然可以获得标准 NSWindow 标题栏附带的所有附加位;可拖动的图标,重命名窗口,用于文档修订的下拉菜单和全屏按钮。

基本上,我使用 setBackgroundColor: 制作了一个黑色窗口:但文本仍然显示为黑色,这在黑色背景上不起作用。

那么有谁知道这样做的方法,或者 Pixelmator 是如何做到的?

【问题讨论】:

  • 我懒得尝试这个,但我想知道如果你得到窗口的内容视图并询问它的超级视图你会得到什么。也许你会得到 nil,或者你会得到一个包含其他隐藏内容的根视图。
  • 你得到了一个私有的 NSView 子类,叫做 NSThemeView。它确实包含一些用于窗口关闭、最小化和最大化按钮的私有视图,但我看不到如何在不使用私有 API 的情况下访问标题。
  • 有一篇 Matt Gallagher 写的关于自定义窗口的文章,你应该可以在那里找到一些信息。 cocoawithlove.com/2008/12/…
  • 问题中提到我不想做一个自定义窗口
  • 我已经发布了答案。检查我的answer here

标签: cocoa nswindow


【解决方案1】:

从 OS X 10.10 开始,将窗口的外观更改为 NSAppearanceNameVibrantDark 就足够了

  window.appearance = NSAppearance(named:NSAppearanceNameVibrantDark)

认为值得一提,因为您能找到的大多数答案都已过时。

【讨论】:

  • 不使用面板...“无法识别的选择器_backstoppedByDefaultAppearance
  • NSAppearanceNameVibrantDark 看起来不错,但它似乎在我的基于 OpenGL 的应用程序中引起了问题......它似乎创建了半透明的窗口栏,并且似乎使用了 CoreAnimation 废话。我只想要一个简单的深色 NSAppearance,没有多余的装饰!
  • 问题是关于 NSWindow,而不是 NSPanel。
【解决方案2】:

这是 Swift 中的一个解决方案。已经很晚了,我很累,所以这可能不是最佳选择,但它有效。

首先,这是一个在层次结构中查找视图的函数,可以选择跳过特定视图。 (如果我们要搜索 window.contentView.superview.subviews 并且我们想忽略您在 contentView 中的观点,这很有用)

func findViewInSubview(subviews: [NSView], #ignoreView: NSView, test: (NSView) -> Bool) -> NSView? {
    for v in subviews {
        if test(v) {
            return v
        } else if v != ignoreView {
            if let found = findViewInSubview(v.subviews as [NSView], ignoreView: ignoreView, test) {
                return found
            }
        }
    }
    return nil
}

下面是您将如何使用它,例如从 NSViewController 子类。请注意,您需要在窗口变得可见时执行此操作,因此您无法在viewDidLoad 中执行此操作。

override func viewDidAppear() {
    if let windowContentView = view.window?.contentView as? NSView {
        if let windowContentSuperView = windowContentView.superview {
            let titleView = findViewInSubview(windowContentSuperView.subviews as [NSView], ignoreView: windowContentView) { (view) -> Bool in
                // We find the title by looking for an NSTextField. You may
                // want to make this test more strict and for example also
                // check for the title string value to be sure.
                return view is NSTextField
            }
            if let titleView = titleView as? NSTextField {
                titleView.attributedStringValue = NSAttributedString(string: "Hello", attributes: [NSForegroundColorAttributeName: NSColor.redColor()])
            }
        }
    }
}

请注意,您是在玩火。像这样的内部结构未指定是有原因的。

【讨论】:

  • 这对我有用,但似乎它绘制了两次文本。第一个文本按照需要正常绘制,但第二个文本有点偏离。这使它看起来非常奇怪。关于如何解决这个问题的任何想法? link
  • 这个方法对我有用。但是你仍然需要在设置 titleView.attributedStringValue 之前设置 NSWindow.title 以便标题文本可以正确布局。
【解决方案3】:

这就是我的做法:

#import <objc/runtime.h>

@interface SOWindow : NSWindow
@end

@interface SOWindowFrameOverrides : NSView
@end

@implementation SOWindow

+ (void)load
{
    SEL selector = NSSelectorFromString(@"_currentTitleColor");
    SEL originalSelector = NSSelectorFromString(@"_original_currentTitleColor");
    Class frameClass = NSClassFromString(@"NSThemeFrame");

    Method m = class_getInstanceMethod(frameClass, selector);
    Method m2 = class_getInstanceMethod([SOWindowFrameOverrides class], selector);
    class_addMethod(frameClass, originalSelector, method_getImplementation(m), method_getTypeEncoding(m));
    method_exchangeImplementations(m, m2);
}

@end

@implementation SOWindowFrameOverrides

- (NSColor *)_currentTitleColor
{
    if ([self.window isKindOfClass:[SOWindow class]]) {
        return [NSColor redColor];
    } else {
        return [self _original_currentTitleColor];
    }
}

- (NSColor *)_original_currentTitleColor
{
    // will be filled in at runtime
    return nil;
}

@end

虽然从 Mavericks 到 Yosemite 的视图层次结构发生了很大变化,但 _currentTitleColor 已在 API 中存在很长时间,并且在不久的将来可能不会改变。即使是艰难的方法调配也是一种 hacky 方式,我发现它比遍历视图层次结构更优雅,但这只是我。

如果您想进一步自定义标题,您可以覆盖 NSThemeFrame 上的 _titleTextField 以返回自定义文本字段(我已使用它来更改我的 F3X 项目的背景样式)。

【讨论】:

    【解决方案4】:

    您可以使用某些私有 API 解决问题,如 cmets 中所述 - 但是,您将无法将该应用提交到 AppStore。

    其他解决方案是获取[[myWindow contentView] superview] - 它为您提供上述NSThemeView 实例。然后,您需要做的就是在视图(实际上称为框架视图)子视图的子视图中搜索 NSTextField 的任何实例并修改它们。请注意,这些私有视图的层次结构可能会随着 OS X 的每个版本而改变,这可能会破坏您的代码。

    可能最好的解决方案是子类(仅子类,没有其他自定义)NSWindow 并实现 -title-setTitle: 方法 - 对于每个窗口,您将实际标题设置为 @""(通过调用[super setTitle:@""]),然后将您自己的、以编程方式创建的NSTextField 实例放入框架视图(即[[[self contentView] superview] addSubview:myTextField],其中myTextField 是文本字段。您需要弄清楚字段,但这是最简单的部分。

    【讨论】:

      猜你喜欢
      • 2011-03-03
      • 2013-11-29
      • 2017-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-24
      相关资源
      最近更新 更多