Fix crash: replace usesAutomaticRowHeights with deferred self-sizing#85
Open
beezly wants to merge 1 commit intoviktorstrate:mainfrom
Open
Fix crash: replace usesAutomaticRowHeights with deferred self-sizing#85beezly wants to merge 1 commit intoviktorstrate:mainfrom
beezly wants to merge 1 commit intoviktorstrate:mainfrom
Conversation
f959378 to
019121b
Compare
NSHostingView inside NSTableView with usesAutomaticRowHeights enters an infinite layout loop: layout() flushes SwiftUI transactions, which invalidate constraints, which re-enter layout. AppKit detects this and crashes with NSGenericException. Replace the entire auto-sizing mechanism with SelfSizingHostingView: - sizingOptions = [] prevents the hosting view from participating in Auto Layout constraint solving. - invalidateIntrinsicContentSize() is overridden to NOT call super (which would re-enter the constraint system) but instead schedule a coalesced async height update via DispatchQueue.main.async. - measureHeight() uses a temporary NSHostingController.sizeThatFits() to properly measure content at the cell width, including text wrapping. - heightOfRow returns cached heights (default 60px). noteHeightOfRows is called with animation duration 0 to avoid visible row resizing. Also removes the old measurementHostingView and handleTableResize.
019121b to
e27d085
Compare
beezly
added a commit
to beezly/mactrix
that referenced
this pull request
Mar 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
NSHostingViewinsideNSTableViewwithusesAutomaticRowHeightsenters an infinite layout loop when SwiftUI view content changes during layout. AppKit detects this and crashes withNSGenericException:The crash reproduces when scrolling into rooms containing video/image messages with reactions or read receipts. It is timing-dependent — more likely when launched from Finder than under a debugger.
Root Cause
During layout,
NSHostingView.layout()flushes SwiftUI transactions. If the SwiftUI view tree has changed (e.g. async content loaded), this callsinvalidateIntrinsicContentSize, which triggers constraint updates, which re-enters layout — exceeding AppKit's constraint update pass limit (one pass per view in the window).This was latent since the NSTableView rewrite (#47) but became reliably triggerable as view complexity grew (reactions, read receipts, video playback).
Fix
Replace the entire auto-sizing mechanism with
SelfSizingHostingView:sizingOptions = []— prevents the hosting view from participating in Auto Layout constraint solvinginvalidateIntrinsicContentSize()overridden to schedule a coalesced async height update instead of calling super (which would re-enter the constraint system)measureHeight()temporarily re-enables sizing, pins a width constraint matching the column width, readsfittingSize, then resets — all outside the layout passheightOfRowreturns cached heights (default 60px);noteHeightOfRowscalled with animation duration 0Also removes the old
measurementHostingView(separateNSHostingControllerper height query) andhandleTableResizeobserver.Depends on