【问题标题】:Updating programmatically set colors on Mode change (dark mode, light mode) on macOS (objective-c)在 macOS 上以编程方式更新模式更改(暗模式、亮模式)设置的颜色(objective-c)
【发布时间】:2020-10-24 08:50:57
【问题描述】:

我使用的是 macOS,objective-c,而不是 iOS。 XCode 12.

在很多视图中,我都设置了这样的颜色:

self.menuIconBar.wantsLayer = YES;
self.menuIconBar.layer.backgroundColor = [NSColor colorNamed:@"color_gradient_right"].CGColor;

每当用户更改外观时,例如到深色模式,我希望我的颜色会根据资产设置而改变:

不幸的是,什么也没发生。 BUT:在 IB 中应用的相同颜色直接按预期变化。我仍然需要他们以编程方式进行更改。

然后我尝试挂钩通知:

[[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(appleInterfaceThemeChangedNotification:) name:@"AppleInterfaceThemeChangedNotification" object:nil];

我收到通知,但是当我再次调用与上面相同的代码时,仍然加载了错误的颜色。

self.menuIconBar.layer.backgroundColor = [NSColor colorNamed:@"color_gradient_right"].CGColor;

任何帮助表示赞赏

【问题讨论】:

  • 您是否尝试过使用这种方法获得通知:- (void)viewDidChangeEffectiveAppearance;
  • 如果你还没有看到,这个 SO 参考可能有用:stackoverflow.com/questions/51672124/…
  • 获取通知不是问题。我希望应用程序采用与我在代码中应用的完全相同的资产颜色的深色外观。查看资产截图
  • 是否使用 WantUpdateLayer = YES;改变什么?
  • 我在视图控制器中,实际上不在视图中。我认为这是 CG 颜色的问题:核心图形结构不是像 NSColor 这样的对象,因此不能动态更改。所以我想知道如何实现这一目标

标签: objective-c macos-darkmode


【解决方案1】:

以下示例将根据系统偏好设置中的外观设置更改自定义视图的背景颜色。它可以通过创建一个 objc 项目、删除预先存在的 App Delegate 并将 'main.m' 中的代码替换为以下代码来在 Xcode 中运行:

#import <Cocoa/Cocoa.h>

@interface CustomView : NSView
@end

@implementation CustomView

- (id)initWithFrame:(NSRect)frameRect {
 if ((self = [super initWithFrame:frameRect]) != nil) {
 // Add initialization code here
 }
 return self;
}
 
- (void)drawRect:(NSRect)rect {
}

- (void)viewDidChangeEffectiveAppearance {
 NSLog (@"appearance did change.");
 NSAppearance *changedAppearance = NSApp.effectiveAppearance;
 NSAppearanceName newAppearance = [changedAppearance bestMatchFromAppearancesWithNames:@[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
 NSLog (@"new appearance name = %@", newAppearance);
  if([newAppearance isEqualToString:NSAppearanceNameDarkAqua]){
    [[self layer] setBackgroundColor:CGColorCreateGenericRGB( 1.0, 0.0, 0.0, 1.0 )];
  } else {
    [[self layer] setBackgroundColor:CGColorCreateGenericRGB( 0.0, 0.0, 1.0, 1.0 )];
  }
}

 // Use this if you want 0,0 (origin) to be top, left
 // Otherwise origin will be at bottom, left (Unflipped)
-(BOOL)isFlipped {
  return YES;
}
@end
 
@interface AppDelegate : NSObject <NSApplicationDelegate> {
 NSWindow *window;
}

 - (void) buildMenu;
 - (void) buildWindow;
@end
 
@implementation AppDelegate
   
- (void) buildMenu {
 NSMenu *menubar = [NSMenu new];
 NSMenuItem *menuBarItem = [NSMenuItem new];
 [menubar addItem:menuBarItem];
 [NSApp setMainMenu:menubar];
 NSMenu *appMenu = [NSMenu new];
 NSMenuItem *quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit"
 action:@selector(terminate:) keyEquivalent:@"q"];
 [appMenu addItem:quitMenuItem];
 [menuBarItem setSubmenu:appMenu];
}
 
- (void) buildWindow {
 #define _wndW  600
 #define _wndH  550
 
 window = [[NSWindow alloc] initWithContentRect: NSMakeRect( 0, 0, _wndW, _wndH )
 styleMask: NSWindowStyleMaskTitled | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskClosable
 backing: NSBackingStoreBuffered defer: NO];
 
 [window center];
 [window setTitle: @"Test window"];
 [window makeKeyAndOrderFront: nil];
 
 // **** CustomView **** //
 CustomView *view = [[CustomView alloc]initWithFrame:NSMakeRect( 20, 60, _wndW - 40, _wndH - 80 )];
 [view setWantsLayer:YES];
 [[view layer] setBackgroundColor:CGColorCreateGenericRGB( 0.0, 0.0, 1.0, 1.0 )];
 [[window contentView] addSubview:view];
 
 // **** Quit btn **** //
 NSButton *quitBtn = [[NSButton alloc]initWithFrame:NSMakeRect( _wndW - 50, 10, 40, 40 )];
 [quitBtn setBezelStyle:NSBezelStyleCircular ];
 [quitBtn setTitle: @"Q" ];
 [quitBtn setAction:@selector(terminate:)];
 [[window contentView] addSubview: quitBtn];
}
 
- (void) applicationWillFinishLaunching: (NSNotification *)notification {
 [self buildMenu];
 [self buildWindow];
}
 
- (void) applicationDidFinishLaunching: (NSNotification *)notification {
}
@end
 
int main() {
 NSApplication *application = [NSApplication sharedApplication];
 AppDelegate *appDelegate = [[AppDelegate alloc] init];
 [application setDelegate:appDelegate];
 [application run];
 return 0;
}

【讨论】:

  • 谢谢。 +1 的努力。但我不想更改颜色,我希望应用程序采用与我在代码中应用的完全相同的资产颜色的深色外观。
  • @Pat_Morita:我也有同样的问题,你解决了吗?
  • 我用这个答案解决了这个问题:stackoverflow.com/questions/56968587/… 关键是改变包含视图的当前外观,因为它们独立于系统外观。
猜你喜欢
  • 2020-01-28
  • 1970-01-01
  • 2020-12-23
  • 2021-02-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-09
  • 2021-09-13
相关资源
最近更新 更多