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

如何防止鼠标事件导致macOS应用中的NSTimer冻结?

解决NSTimer在按住NSButton/NSSlider时停止更新的问题

根本原因是你创建的定时器默认只绑定到NSDefaultRunLoopMode,而当按住按钮或拖拽滑块时,App的RunLoop会切换到NSEventTrackingRunLoopMode,此时默认模式下的定时器就会暂停执行。不需要子类化控件,只需要修改定时器的RunLoop模式即可解决问题。

具体实现步骤

  • 不要使用系统提供的scheduledTimer(withTimeInterval:...)方法创建定时器(这个方法默认把定时器加到NSDefaultRunLoopMode),而是手动创建定时器后,将其添加到RunLoop的common模式集合中。

Swift 代码示例:

// 定义定时器属性(需要持有引用,避免被释放)
var updateTimer: Timer?

// 在合适的地方(比如viewDidLoad)初始化并添加定时器
func setupTimer() {
    updateTimer = Timer(timeInterval: 0.1, target: self, selector: #selector(updateTextFieldValue), userInfo: nil, repeats: true)
    // 将定时器添加到RunLoop的common模式
    if let timer = updateTimer {
        RunLoop.current.add(timer, forMode: .common)
    }
}

// 定时器触发的方法
@objc func updateTextFieldValue() {
    // 更新你的NSTextField数值逻辑
    yourTextField.stringValue = "\(Date())"
}

// 记得在合适的时机(比如viewWillDisappear)销毁定时器
func teardownTimer() {
    updateTimer?.invalidate()
    updateTimer = nil
}

闭包形式的定时器(Swift 3.2+):

var updateTimer: Timer?

func setupTimer() {
    updateTimer = Timer(timeInterval: 0.1, repeats: true) { [weak self] _ in
        guard let self = self else { return }
        // 更新数值逻辑
        self.yourTextField.stringValue = "\(Date())"
    }
    if let timer = updateTimer {
        RunLoop.current.add(timer, forMode: .common)
    }
}

原理说明

RunLoop.Mode.common是一个模式集合,包含了default和eventTracking这两种常用模式。把定时器加到这个集合后,无论App处于正常事件处理模式,还是控件拖拽/按住的事件追踪模式,定时器都会正常触发,完全不需要修改任何控件的子类实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:00:59