macOS “Big Sur” Detect dark menu-bar/system tray

后端 未结 2 1239
长发绾君心
长发绾君心 2020-12-31 15:07

Starting with macOS (10.16 "Beta"/11.0) "Big Sur", the menu-bar and system tray no longer honor the Desktop dark-mode preference, making it difficult to

2条回答
  •  春和景丽
    2020-12-31 15:31

    I just submitted a TSI and I got an answer:

    But I would not add an NSView on top of the NSStatusItem’s button directly. The docs mention to use the button property to customize the appearance and behavior of the status item, but it should work within the confines of the button itself, that is, it’s various properties like the image and text and their placements. Years ago, NSStatusItem allowed for custom views, but then became deprecated, in order to support a button-based UI, therefore allowing its drawing behavior to easily adapt to changes in the menu bar’s appearance.

    So unfortunately there is no way to get this information programatically. However, getting information is very important for three of my apps, so I went exploring.

    For me personally, it was very important that getting this information will not trigger any security prompts.

    I came up with the following idea:

    • Create and hide a NSStatusItem
    • Set a templated NSImage
    • Render the contents of the CALayer into a NSImage

    You can use this code to get the colour information (note: the NSStatusItem is never visible and does not cause existing items to move or something like that). Feel free to adjust the formatting and classes:

    I have created a class called MenuBar with a public property:

    public class MenuBar {
        private static var statusItem: NSStatusItem?
    
        public static var theme: MenuBarTheme {
            if self.statusItem == nil {
                self.statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
                self.statusItem?.button?.image = NSImage(systemSymbolName: "circle.fill", accessibilityDescription: nil)
                self.statusItem?.isVisible = false
            }
            
            if let color = self.getPixelColor() {
                return color.redComponent < 0.20 && color.blueComponent < 0.20 && color.greenComponent < 0.20 ? .light : .dark
            }
            else
            {
                return NSApplication.isDarkMode ? .dark : .light
            }
        }
    
        public static var tintColor: NSColor {
            return self.theme == .light ? NSColor.black : NSColor.white
      }
        
      // MARK: - Helper
      fileprivate static func getPixelColor() -> NSColor?
      {
         if let image = self.statusItem?.button?.layer?.getBitmapImage() {
            let imageRep = NSBitmapImageRep(data: image.tiffRepresentation!)
                
             if let color = imageRep?.colorAt(x: Int(image.size.width / 2.0), y: Int(image.size.height / 2.0)) {
                return color
             }
         }
            
         return nil
      }
    }
    
    public enum MenuBarTheme : String
    {
        case light = "light"
        case dark = "dark"
    }
    
    public extension NSApplication
    {
        class var isDarkMode: Bool
        {
            return NSApplication.shared.appearance?.description.lowercased().contains("dark") ?? false
        }
    }
    
    public extension CALayer
    {
        func getBitmapImage() -> NSImage
        {
            let btmpImgRep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(self.frame.width), pixelsHigh: Int(self.frame.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: .deviceRGB, bytesPerRow: 0, bitsPerPixel: 32)
    
            let ctx = NSGraphicsContext(bitmapImageRep: btmpImgRep!)
            let cgContext = ctx!.cgContext
            
            self.render(in: cgContext)
            
            let cgImage = cgContext.makeImage()
    
            return NSImage(cgImage: cgImage!, size: CGSize(width: self.frame.width, height: self.frame.height))
        }
    }
    

提交回复
热议问题