fix(macos): defer isOverflowing mutation to break SwiftUI render loop (fixes #43480) (#92778)

* fix(macos): defer isOverflowing mutation to break SwiftUI render loop

measuredHeight() mutated model.isOverflowing synchronously during a SwiftUI
view update cycle. The onChange(of: attributed) handler triggered
updateWindowFrame → targetFrame → measuredHeight, which set isOverflowing,
invalidating the view and re-triggering onChange — an infinite render loop
causing 100% CPU pinwheel.

Fix: defer the isOverflowing mutation via DispatchQueue.main.async with an
equality guard to prevent redundant updates. The frame calculation itself
remains synchronous so the window size is correct immediately.

Fixes #43480

* fix(macos): preserve latest overflow measurement

---------

Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
This commit is contained in:
liuhao1024
2026-06-14 08:56:25 +08:00
committed by GitHub
parent ac3a98e55d
commit fd4f5b3f59
@@ -92,7 +92,13 @@ extension VoiceWakeOverlayController {
let contentHeight = ceil(used.height + (textInset.height * 2))
let total = contentHeight + self.verticalPadding * 2
self.model.isOverflowing = total > self.maxHeight
// Defer the overflow state mutation to break the SwiftUI onChange measuredHeight
// isOverflowing re-render onChange synchronous render loop (fixes #43480).
let overflowing = total > self.maxHeight
DispatchQueue.main.async { [weak self] in
guard let self, self.model.isOverflowing != overflowing else { return }
self.model.isOverflowing = overflowing
}
return max(self.minHeight, min(total, self.maxHeight))
}