<!--
{
  "documentType" : "article",
  "framework" : "UIKit",
  "identifier" : "/documentation/UIKit/managing-viewport-layout-and-attachment-reuse-in-a-text-view-subclass",
  "metadataVersion" : "0.1.0",
  "role" : "article",
  "title" : "Managing viewport layout and attachment reuse in text views"
}
-->

# Managing viewport layout and attachment reuse in text views

Customize layout and preserve attachment views in your text view subclass.

## Overview

If you build a text editor with rich attachments — like images, videos, or interactive controls — you may notice that attachment views flicker or lose their state when someone scrolls the attachment out of the viewport, or types in the same paragraph. This is because text views discard and recreate attachment views as part of their normal layout process.

[`UITextView`](/documentation/UIKit/UITextView) and <doc://com.apple.documentation/documentation/AppKit/NSTextView> conform to [`NSTextViewportLayoutControllerDelegate`](/documentation/UIKit/NSTextViewportLayoutControllerDelegate), giving your subclass direct access to the layout process. You can override the delegate methods to respond to layout events and customize how text fragments appear.

You can also register reuse policies to tell the text view which attachment views to preserve across scrolling and editing, eliminating flicker and preserving states like focus and playback position.

### Override viewport layout methods

[`layoutViewport()`](/documentation/UIKit/NSTextViewportLayoutController/layoutViewport()) encompasses the viewport layout process. Each time the visual state within the viewport changes, TextKit calls it. During each viewport layout process, [`NSTextViewportLayoutController`](/documentation/UIKit/NSTextViewportLayoutController) calls the text view’s delegate methods at three points — before it starts, before each text layout fragment is about to be rendered, and after it completes. Override any of these methods in your [`UITextView`](/documentation/UIKit/UITextView) (or <doc://com.apple.documentation/documentation/AppKit/NSTextView> in macOS) subclass to customize what happens at each stage:

```swift
class CustomTextView: UITextView {

    override func textViewportLayoutControllerWillLayout(
        _ textViewportLayoutController: NSTextViewportLayoutController
    ) {
        super.textViewportLayoutControllerWillLayout(textViewportLayoutController)
        // Prepare any state before the layout pass begins.
    }

    override func textViewportLayoutController(
        _ textViewportLayoutController: NSTextViewportLayoutController,
        configureRenderingSurfaceFor textLayoutFragment: NSTextLayoutFragment
    ) {
        super.textViewportLayoutController(textViewportLayoutController, configureRenderingSurfaceFor: textLayoutFragment)
        // Inspect or customize each laid-out text fragment.
    }

    override func textViewportLayoutControllerDidLayout(
        _ textViewportLayoutController: NSTextViewportLayoutController
    ) {
        super.textViewportLayoutControllerDidLayout(textViewportLayoutController)
        // Finalize layout after all fragments are positioned.
    }
}
```

> Important: All overrides of viewport layout delegate methods must call `super`, because omitting it breaks layout. Query any ``doc://com.apple.uikit/documentation/UIKit/NSTextViewportLayoutController`` state only inside ``doc://com.apple.uikit/documentation/UIKit/UITextView/textViewportLayoutControllerDidLayout(_:)``; querying it from other delegate methods can cause crashes.

The following table shows the set of available override points:

|Method                                                                                                                |When called                                           |
|----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|
|``doc://com.apple.uikit/documentation/UIKit/UITextView/textViewportLayoutControllerWillLayout(_:)``                   |Before each layout pass begins                        |
|``doc://com.apple.uikit/documentation/UIKit/UITextView/textViewportLayoutController(_:configureRenderingSurfaceFor:)``|Once per laid-out fragment, with its rendering surface|
|``doc://com.apple.uikit/documentation/UIKit/UITextView/textViewportLayoutControllerDidLayout(_:)``                    |After each layout pass completes                      |
|``doc://com.apple.uikit/documentation/UIKit/UITextView/textViewportLayoutControllerReceivedSetNeedsLayout(_:)``       |When the viewport requests a layout invalidation      |
|``doc://com.apple.uikit/documentation/UIKit/UITextView/viewportBounds(for:)``                                         |To query the current viewport bounds                  |

### Register reuse policies

Use the registration API to preserve attachment views across layout passes. Call [`register(_:forTextAttachmentViewProviderType:)`](/documentation/UIKit/UITextView/register(_:forTextAttachmentViewProviderType:)) once during setup to declare the reuse behavior per provider type:

```swift
// Retain video attachment views across both scrolling and paragraph edits.
textView.register(
    [.onScrollingOutOfViewport, .onEditingInlineParagraphs],
    forTextAttachmentViewProviderType: VideoAttachmentViewProvider.self
)

// Retain drawing views only when they scroll out of view.
textView.register(
    [.onScrollingOutOfViewport],
    forTextAttachmentViewProviderType: DrawingAttachmentViewProvider.self
)
```

After you register a provider type, the text view holds on to those attachment views when scrolling moves them out of view or editing changes the surrounding paragraph, and restores them when they come back. This automatically keeps video playback position and focused controls intact across scrolling and paragraph edits.

---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)