如何避免macOS窗口调整大小时onHover触发错误信号?
解决SwiftUI窗口缩放/全屏时onHover误触发问题
当窗口缩放或切换全屏模式后,onHover会短暂触发false再变回true,这是视图重绘过程中临时丢失hover状态导致的。要实现可靠的光标进入/退出检测,可通过**防抖(Debounce)**过滤短时间内的状态波动。
方案一:Combine防抖实现
利用Combine的debounce操作符,仅在状态稳定一段时间后处理最终hover状态:
import SwiftUI import Combine struct ContentView: View { @State private var isHoveringState = false private let hoverDebounce = PassthroughSubject<Bool, Never>() private var cancellables = Set<AnyCancellable>() init() { hoverDebounce .debounce(for: .milliseconds(100), scheduler: DispatchQueue.main) .sink(receiveValue: { newValue in isHoveringState = newValue print(newValue) // 在这里处理最终的hover逻辑 }) .store(in: &cancellables) } var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .frame(maxWidth: .infinity, maxHeight: .infinity) .onHover { isHovering in hoverDebounce.send(isHovering) } } } #Preview { ContentView() }
方案二:Timer防抖实现
若不想引入Combine,可手动用Timer实现防抖逻辑:
struct ContentView: View { @State private var isHoveringState = false private var hoverTimer: Timer? var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .frame(maxWidth: .infinity, maxHeight: .infinity) .onHover { isHovering in // 每次状态变化时取消之前的定时器 hoverTimer?.invalidate() // 100毫秒后确认最终状态 hoverTimer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: false) { _ in isHoveringState = isHovering print(isHoveringState) // 处理最终hover逻辑 } } } } #Preview { ContentView() }
说明
- 防抖时间设为100毫秒,既可以过滤窗口调整带来的临时状态波动,又不会影响正常hover交互,可根据需求自行调整时间值。
- 两种方案都能确保仅当光标状态稳定后,才触发业务逻辑,避免窗口调整时的误触发。
内容的提问来源于stack exchange,提问作者Mark
相关产品推荐
相关产品推荐

