To fix SwiftUI content that drifts offscreen during Mac window resizing, first identify whether drawing moves beyond its layout bounds, content refuses to shrink, or an embedded AppKit view stops tracking its container. Then repair that specific boundary.

This guide is for a developer fixing their own Mac app’s resizable interface: keeping labels readable, controls reachable, and longer content scrollable. The diagnostic sequence below uses synthetic labels and distinguishes SwiftUI layout, window sizing, and AppKit text wrapping.

Identify which boundary fails during resizing

SwiftUI layout begins with a size proposal from a parent. A child chooses its size in response; the proposal is not an instruction to clip everything to that rectangle. Apple documents minimum, maximum, and ideal-size proposals in ProposedViewSize. This explains why making a window narrower does not necessarily make every descendant narrower.

Start with a repeatable test: open your app’s window, narrow it until the first failure appears, then widen it again. Observe the first affected element rather than the final arrangement. Does a label extend beyond its container? Does the entire panel shift? Does text retain its previous wrapping width?

Temporarily add .border(Color.orange) to the suspect child and .border(Color.blue) to its enclosing frame or container. Place the modifiers at different levels so you can compare their rectangles. Apple’s offset documentation demonstrates this border technique to distinguish an original layout position from displaced drawing.

What you observe Inspect first Useful next action
Drawing moves outside an otherwise stable rectangle offset, position, coordinate calculations Remove placement modifiers individually
A label stays wide as its container narrows Horizontal fixedSize, explicit widths Let the label accept a constrained width
Controls cannot fit in a horizontal row Combined control widths and spacing Provide a vertical alternative
Lower content disappears as height decreases Missing overflow behavior Make the content region scrollable
A hosted SwiftUI panel shifts within AppKit Hosting view bounds and sizing constraints Compare host size with content requirements
An AppKit text view narrows but lines stay wide Text container width tracking Check text-view and text-container configuration

These are diagnostic hypotheses derived from the documented behaviors, not a guarantee that a particular symptom has one cause. Change one suspect at a time and repeat the same resize gesture. If replacing a complex child with Text("Sample panel") removes the failure, restore that child’s layers gradually.

Remove placement and sizing rules that resist the window

Check offsets and center-based positioning

An offset shifts displayed content without changing the view’s original dimensions. A positive horizontal offset can therefore push drawing past the right edge even though the parent still reserves the original space. Remove the offset temporarily; if the drawing returns inside the container, replace the adjustment with an arrangement expressed through stacks, padding, or frame alignment. See Apple’s offset behavior.

position(x:y:) places the view’s center at coordinates in its parent’s coordinate space. Treating those coordinates as the upper-left corner introduces an error related to the child’s size. A position calculated once can also become unsuitable when its parent changes size. For an ordinary label-and-controls panel, prefer alignment that expresses the intended relationship to the container. Apple defines the center-based behavior in position(x:y:).

For example, .frame(maxWidth: .infinity, alignment: .leading) expresses leading alignment within a flexible frame. It does not remove an oversized descendant’s own sizing requirements. Apple’s flexible frame documentation explains how the frame constrains proposals and positions its child.

Check fixed widths and ideal-size text

Inspect explicit width settings and horizontal uses of fixedSize along the failing branch. Apple specifically documents that .fixedSize(horizontal: true, vertical: false) makes text preserve the ideal width needed for the entire string, potentially exceeding its parent’s bounds. For wrapping body text, remove that horizontal requirement. Where preserving the wrapped text’s full height is appropriate, consider .fixedSize(horizontal: false, vertical: true). See fixedSize(horizontal:vertical:).

Preserving vertical size requires somewhere for that height to go. A vertically growing paragraph inside a short, non-scrolling container can still become unreachable. Use a vertical scroll region when the task requires access to all of the text; Apple documents the supported scrolling axes in ScrollView.

Recheck geometry-based calculations

GeometryReader defines content using its own size and coordinate space, and returns a flexible preferred size to its parent. Adding one around a view changes the layout structure, so it is not a neutral measurement wrapper. If drift began after introducing geometry calculations, temporarily replace that branch with ordinary stacks and frames. Apple describes these properties in GeometryReader.

For a proportional layout that genuinely needs geometry, derive dimensions from the relevant current container. Inspect any state that stores a width captured during initial appearance. Also check whether the measured dimension is used to resize the same region being measured; removing that dependency is a useful isolation test.

Build a compact layout with explicit overflow behavior

The following complete sample demonstrates three independent choices: text wraps, controls switch arrangement when needed, and vertical overflow remains reachable. Its dimensions are illustrative design inputs, not Apple-prescribed values.

Create a macOS SwiftUI app in Xcode, set its deployment target to macOS 13 or later, and replace the generated app and content-view declarations with this single file. Keep only one @main declaration. The sample uses ViewThatFits and window resizability APIs available on macOS 13. Apple documents their behavior in ViewThatFits and windowResizability(_:).

import SwiftUI

@main
struct ResizeExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ResizeExampleView()
                .frame(minWidth: 280, minHeight: 220)
        }
        .windowResizability(.contentMinSize)
    }
}

struct ResizeExampleView: View {
    @State private var selection = "Sample A"

    private let details = Array(
        repeating: "Sample notes wrap as the panel becomes narrower.",
        count: 12
    ).joined(separator: " ")

    var body: some View {
        ScrollView(.vertical) {
            VStack(alignment: .leading, spacing: 16) {
                Text("Sample panel")
                    .font(.title2)

                Text(details)
                    .fixedSize(horizontal: false, vertical: true)
                    .frame(maxWidth: .infinity, alignment: .leading)

                ViewThatFits(in: .horizontal) {
                    HStack(spacing: 12) {
                        Button("Choose Sample A") {
                            selection = "Sample A"
                        }
                        Button("Choose Sample B") {
                            selection = "Sample B"
                        }
                    }
                    .fixedSize(horizontal: true, vertical: false)

                    VStack(alignment: .leading, spacing: 12) {
                        Button("Choose Sample A") {
                            selection = "Sample A"
                        }
                        Button("Choose Sample B") {
                            selection = "Sample B"
                        }
                    }
                }

                Text("Selected: \(selection)")
            }
            .padding(20)
            .frame(
                minWidth: 0,
                maxWidth: .infinity,
                alignment: .leading
            )
        }
    }
}

ViewThatFits evaluates alternatives in their declared order and chooses the first whose ideal size fits along the constrained axes. Here, horizontal fitting selects between the button row and the vertical stack. The row deliberately preserves its horizontal ideal size so that a compressed row does not become the preferred result. Keep both alternatives equivalent in available actions. See Apple’s selection rules.

The outer frame permits the content column to follow the available width, while the vertical scroll view supplies access when wrapping increases height. This arrangement assumes the individual controls fit at the chosen minimum width. Longer labels or larger fonts may require a different minimum or a different compact arrangement.

This sample was inspected for consistent declarations and API availability; it was not compiled here because a Swift compiler was unavailable. Run it on macOS to verify the actual window and text behavior.

Set window limits after fixing the content layout

A useful minimum window size prevents an interface from becoming smaller than its supported arrangement. With .contentMinSize, SwiftUI derives the window’s minimum size from its content and imposes no maximum size. In the sample, the root frame supplies minimum width and height inputs. See contentMinSize.

Choose that minimum by testing your compact arrangement with its longest supported labels. Increasing the minimum until a misplaced view becomes visible can conceal the original layout error. First remove unintended displacement and oversized children; then set the smallest dimensions at which the intended task remains usable.

If AppKit owns the window and hosts SwiftUI, inspect the hosting boundary separately. On macOS 13 and later, NSHostingView.sizingOptions controls constraints derived from SwiftUI content. When the host is the window’s contentView, it also updates the window’s content minimum and maximum sizes. Apple further documents that content is centered when the host’s frame differs from the size required by its SwiftUI content. Check these relationships before adding compensating offsets. See NSHostingView sizingOptions.

Check AppKit and TextKit only when the failing view uses them

Let SwiftUI size the represented root view

For an NSViewRepresentable, SwiftUI controls the represented AppKit view’s frame and bounds. Apple explicitly warns that setting those properties yourself conflicts with SwiftUI and produces undefined behavior. If your resize handler repeatedly assigns the represented root’s frame, remove that competing ownership and express sizing through the SwiftUI boundary. See NSViewRepresentable.

When custom measurement is necessary, macOS 13 and later provides sizeThatFits(_:nsView:context:). It can receive several proposals during one layout pass; returning nil requests default sizing. Make the result depend on the current proposal and content rather than a previous window width. An unspecified proposed dimension needs an intentional measurement policy, rather than being treated automatically as zero. See representable sizing.

Swift’s optional binding provides a way to check for and extract a proposed dimension before using it. This is useful when implementing a sizing policy without force-unwrapping optional widths or heights. The language behavior is documented in The Basics: Optional Binding.

Separate text wrapping width from text-view width

If the failing child is an NSTextView, compare its current width with its text container’s width. Apple documents that NSTextContainer.widthTracksTextView defaults to false; setting it to true makes the container adjust its width when the text view’s width changes. That is the relevant control when lines should reflow with the text view. See widthTracksTextView.

Also inspect isHorizontallyResizable. It controls whether the text view changes width to fit its text, which is a different requirement from wrapping text inside an assigned width. For a vertically scrolling, wrapping text region, configure those two policies consistently and ensure the containing layout actually resizes the text view. See isHorizontallyResizable.

Preserve the text engine your implementation uses. Apple documents that accessing layoutManager on an NSTextView or its text container can switch it into compatibility mode using NSLayoutManager. Do not add that access casually while diagnosing a TextKit 2 view. Use the appropriate APIs for the existing engine and deployment target. See NSTextView.

Verify the repair across width and height changes

Use this final sequence in your own app:

  1. Narrow the window slowly through the point that originally failed, then widen it again.
  2. Reduce height independently and confirm that all required content remains reachable.
  3. Repeat with long synthetic labels and multiline text.
  4. Activate each control in both wide and compact arrangements.
  5. For an AppKit text view, confirm that wrapping follows the current width in both resize directions.

A successful repair keeps placement tied to the current container, gives content a workable compact arrangement, and provides access to overflow. Once those behaviors hold across repeated resizing, remove the temporary borders and retain the minimum window size justified by the interface.

Sources