You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS状态栏图标与文本布局优化及动态文本更新问题求助

解决macOS状态栏文本动态更新的问题

我来帮你搞定这个动态更新状态栏文本的需求!你的核心问题是只在初始化时读取了一次percentualDownload的值,没有监听它的变化,所以当UserPreferences里的这个属性更新时,状态栏文本不会同步刷新。下面是具体的修复方案:

问题分析

原代码中,你在StatusBarController的init方法里只设置了一次statusItem.button?.title,而UserPreferences里的percentualDownload是用@Published标记的可观察属性,需要通过订阅它的变化来实时更新UI。另外,你原代码里判断inDownload的逻辑也有问题(错误地访问了$inDownload这个Publisher,而不是实际的属性值)。

修复后的代码

修改StatusBarController

import AppKit
import SwiftUI
import Combine // 别忘了导入Combine框架

class StatusBarController {
    @ObservedObject var userPreferences = UserPreferences.instance
    private var statusBar: NSStatusBar
    private var statusItem: NSStatusItem
    private var popover: NSPopover
    // 添加这个集合来保存订阅,防止被提前释放
    private var cancellables = Set<AnyCancellable>()

    init(_ popover: NSPopover) {
        self.popover = popover
        statusBar = NSStatusBar.init()
        statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
        
        if let statusBarButton = statusItem.button {
            // 初始化时先设置一次初始值
            updateStatusBarTitle()
            
            statusBarButton.image = #imageLiteral(resourceName: "Weather")
            statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0)
            statusBarButton.image?.isTemplate = true
            statusBarButton.action = #selector(togglePopover(sender:))
            statusBarButton.target = self
            statusBarButton.imagePosition = NSControl.ImagePosition.imageLeft
        }
        
        // 订阅percentualDownload的变化,实时更新标题
        userPreferences.$percentualDownload
            .receive(on: DispatchQueue.main) // 确保在主线程更新UI
            .sink { [weak self] _ in
                self?.updateStatusBarTitle()
            }
            .store(in: &cancellables)
        
        // 如果需要监听inDownload的变化,也可以添加订阅
        userPreferences.$inDownload
            .receive(on: DispatchQueue.main)
            .sink { [weak self] _ in
                self?.updateStatusBarTitle()
            }
            .store(in: &cancellables)
    }
    
    // 提取单独的方法来更新标题,避免重复代码
    private func updateStatusBarTitle() {
        if userPreferences.inDownload {
            statusItem.button?.title = userPreferences.percentualDownload
        } else {
            // 这里可以设置下载完成后的文本,比如"完成"或者空字符串
            statusItem.button?.title = "完成"
        }
    }
    
    @objc func togglePopover(sender: AnyObject) {
        if(popover.isShown) {
            hidePopover(sender)
        } else {
            showPopover(sender)
        }
    }
    
    func showPopover(_ sender: AnyObject) {
        if let statusBarButton = statusItem.button {
            popover.show(relativeTo: statusBarButton.bounds, of: statusBarButton, preferredEdge: NSRectEdge.maxY)
        }
    }
    
    func hidePopover(_ sender: AnyObject) {
        popover.performClose(sender)
    }
}

关键改动说明

  1. 导入Combine框架:因为我们要使用@Published的订阅功能,必须导入Combine。
  2. 添加cancellables集合:用来保存订阅对象,防止订阅被Swift的ARC机制提前释放。
  3. 提取updateStatusBarTitle方法:把设置标题的逻辑单独抽出来,避免在初始化和订阅回调里重复写代码,也方便后续维护。
  4. 订阅属性变化:通过userPreferences.$percentualDownload.sink()来监听属性的每一次变化,并且切换到主线程更新UI(因为UI操作必须在主线程执行)。
  5. 修正inDownload的判断逻辑:直接访问userPreferences.inDownload属性,而不是它的Publisher对象$inDownload。

这样修改后,每当UserPreferences里的percentualDownload或者inDownload发生变化时,状态栏的文本都会自动同步更新啦!

内容的提问来源于stack exchange,提问作者Paul

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 21:14:05