Layout Manager Selection Guide
Use when the main task is choosing between TextKit 1 and TextKit 2, especially NSLayoutManager versus NSTextLayoutManager for performance, migration risk, large documents, or feature fit. Reach for this when the stack choice is still open, not when the user already needs API-level mechanics.
Use when the main task is choosing between TextKit 1 and TextKit 2, especially NSLayoutManager versus NSTextLayoutManager for performance, migration risk, large documents, or feature fit. Reach for this when the stack choice is still open, not when the user already needs API-level mechanics.
Family: View And Stack Decisions
Use this skill when the main question is whether the editor should use TextKit 1 or TextKit 2.
When to Use
Section titled “When to Use”- You are choosing between
NSLayoutManagerandNSTextLayoutManager. - You are evaluating migration risk or performance tradeoffs.
- You need a recommendation tied to feature requirements.
Quick Decision
Section titled “Quick Decision”Need glyph-level access? YES → NSLayoutManager (TextKit 1)
Need multi-page/multi-column layout? YES → NSLayoutManager (TextKit 1)
Need text tables (NSTextTable)? YES → NSLayoutManager (TextKit 1)
Need Writing Tools inline experience? YES → NSTextLayoutManager (TextKit 2)
Document > 10K lines and performance-critical? → Read the Performance Evidence section carefully
Building new app, iOS 16+? → Start with TextKit 2 (default). Fall back to TK1 only if needed.
Maintaining legacy app? → Stay on TextKit 1 unless you have a specific reason to migrate.Core Guidance
Section titled “Core Guidance”The Two Layout Managers
Section titled “The Two Layout Managers”| Aspect | NSLayoutManager (TK1) | NSTextLayoutManager (TK2) |
|---|---|---|
| Available | iOS 7+ / macOS 10.0+ | iOS 15+ / macOS 12+ |
| Layout model | Contiguous (optional non-contiguous) | Always non-contiguous (viewport) |
| Abstraction | Glyph-based | Element/fragment-based |
| Text containers | Multiple | Single only |
| Performance model | O(document) or O(visible) | O(viewport) theoretical |
| International text | Manual glyph handling | Correct by design |
| Writing Tools | Panel only | Full inline |
| Custom rendering | Subclass + drawGlyphs | Subclass NSTextLayoutFragment |
| Overlay styling | Temporary attributes | Rendering attributes |
| Printing | Full support | Limited (iOS 18+) |
Performance Evidence
Section titled “Performance Evidence”Apple’s Claims (WWDC21)
Section titled “Apple’s Claims (WWDC21)”“TextKit 2 is extremely fast for an incredibly wide range of scenarios, from quickly rendering labels that are only a few lines each to laying out documents that are hundreds of megabytes being scrolled through at interactive rates.”
Developer Experience (Real-World)
Section titled “Developer Experience (Real-World)”ChimeHQ TextViewBenchmark (macOS 14 beta):
“TextKit 1 is extremely fast and TextKit 2 actually even a small amount faster.”
For comparable document sizes on recent macOS, TextKit 2 has reached parity or slightly better.
Large document scrolling (Apple Developer Forums, multiple reports):
- TextKit 2 scrolling performance degrades above ~3,000 lines
- 10K+ lines described as “an absolute nightmare” with TextKit 2
- Switching to TextKit 1 restored smooth scrolling with 1 million characters
- These reports are primarily from iOS 16/17 era; improvements in each release
Memory usage (developer measurement):
- ~0.5 GB with TextKit 1 custom labels
- ~1.2 GB with TextKit 2 for same content
- TextKit 2’s immutable object model (NSTextLayoutFragment, NSTextLineFragment) has overhead
Apple’s own apps:
- Pages, Xcode, Notes: still use TextKit 1 (as of reports through 2025)
- TextEdit: uses TextKit 2 but falls back for tables, page layout, printing
Short Text (Labels, Chat Bubbles)
Section titled “Short Text (Labels, Chat Bubbles)”TextKit 2 wins here:
- No performance penalty for viewport management on short text
- Correctness benefits for international text
- Modern API with rendering attributes
Large Documents (10K+ lines)
Section titled “Large Documents (10K+ lines)”Mixed results:
- Theoretical advantage: Viewport layout should scale to any document size
- Practical issues: Scroll bar jiggle (estimated heights), jump-to-position inaccuracy, height estimation instability
- Improving per OS release: macOS 14+ shows near-parity in benchmarks
- Recommendation: Test on your target OS version with your actual content
Line Counting
Section titled “Line Counting”Both systems struggle with this:
- TextKit 1:
numberOfGlyphs+ enumeration. Requires full layout withallowsNonContiguousLayout = false, or approximate with non-contiguous. - TextKit 2: Must enumerate ALL layout fragments with
.ensuresLayout. Defeats viewport optimization.
For either system: Consider maintaining a separate line count (incremental update on edit) rather than querying the layout system.
When TextKit 1 Is the Right Choice
Section titled “When TextKit 1 Is the Right Choice”- Glyph-level access — Custom glyph substitution, glyph inspection, typography tools
- Multi-page/multi-column layout — NSTextLayoutManager supports only one container
- Text tables — NSTextTable/NSTextTableBlock are TextKit 1 only
- Proven reliability for large documents — Decades of battle-testing
- Need printing — Better pagination control (though TextKit 2 printing improving)
- Custom NSLayoutManager subclass — Significant investment in
drawGlyphs,drawBackground, delegate methods shouldGenerateGlyphsdelegate — No TextKit 2 equivalent- Exact document height required — TextKit 1 contiguous layout gives exact height; TextKit 2 estimates
- Scroll bar accuracy critical — TextKit 2’s estimated heights cause scroll bar instability
When TextKit 2 Is the Right Choice
Section titled “When TextKit 2 Is the Right Choice”- New iOS 16+ app — It’s the default; fighting it adds complexity
- Writing Tools (full inline) — Requires TextKit 2
- International text correctness — Arabic, Devanagari, CJK handled correctly without glyph assumptions
- Custom rendering via fragments — Cleaner API than drawGlyphs subclassing
- Short text (labels, chat bubbles) — No downside, cleaner API
- Viewport-based display of large content — When you don’t need exact document metrics
- Custom text elements — NSTextContentManager subclass for non-attributed-string backends
Migration Decision Framework
Section titled “Migration Decision Framework”Don’t Migrate If:
Section titled “Don’t Migrate If:”- App works well with TextKit 1
- You rely on glyph APIs extensively
- You need multi-container layout
- No specific TextKit 2 feature is required
- Target OS includes iOS 15 or earlier
Consider Migrating If:
Section titled “Consider Migrating If:”- Users need Writing Tools inline experience
- International text rendering issues in TextKit 1
- Building new text features from scratch
- Want viewport performance for very large documents
- Need rendering attributes (cleaner than temporary attributes)
Migration Strategy
Section titled “Migration Strategy”- Check
textLayoutManagerfirst — write all new code to check TextKit 2 availability - Dual code paths — support both TK1 and TK2 during transition
- Test fallback — ensure your app handles fallback gracefully
- Migrate incrementally — one feature at a time, not big bang
// Dual code path patternif let textLayoutManager = textView.textLayoutManager { // TextKit 2 path textLayoutManager.enumerateTextLayoutFragments(from: nil, options: [.ensuresLayout]) { fragment in // ... return true }} else { // TextKit 1 fallback let layoutManager = textView.layoutManager! layoutManager.ensureLayout(for: textView.textContainer) // ...}Common Pitfalls
Section titled “Common Pitfalls”- Migrating without a reason — TextKit 1 works. Don’t fix what isn’t broken.
- Assuming TextKit 2 is always faster — Real-world performance depends on document size, OS version, and use case.
- Not testing on target OS — TextKit 2 performance improves each release. Test on YOUR minimum deployment target.
- Full-document
ensureLayoutin TextKit 2 — Defeats viewport optimization. O(document_size). - Expecting exact scroll metrics from TextKit 2 — Estimated heights cause scroll bar instability. If exact metrics matter, use TextKit 1.
Documentation Scope
Section titled “Documentation Scope”This page documents the apple-text-layout-manager-selection decision skill. Use it when the main task is choosing the right Apple text API, view, or architecture.
Related
Section titled “Related”apple-text-textkit1-ref: Use when the user is already on TextKit 1 and needs exact NSLayoutManager, NSTextStorage, or NSTextContainer APIs, glyph and layout lifecycle details, temporary attributes, exclusion paths, or multi-container behavior. Reach for this when the stack choice is already made and the task is reference-level TextKit 1 mechanics, not stack selection or symptom-first debugging.apple-text-textkit2-ref: Use when the user is already on TextKit 2 and needs exact NSTextLayoutManager, NSTextContentManager, NSTextContentStorage, viewport layout, fragment, rendering-attribute, or migration details. Reach for this when the stack choice is already made and the task is reference-level TextKit 2 mechanics, not stack selection or generic text-system debugging.apple-text-fallback-triggers: Use when the user needs to know exactly what makes TextKit 2 fall back to TextKit 1, or wants to audit code for fallback risk before it ships. Reach for this when the question is specifically about compatibility-mode triggers, not general text-system debugging.
Full SKILL.md source
---name: apple-text-layout-manager-selectiondescription: Use when the main task is choosing between TextKit 1 and TextKit 2, especially NSLayoutManager versus NSTextLayoutManager for performance, migration risk, large documents, or feature fit. Reach for this when the stack choice is still open, not when the user already needs API-level mechanics.license: MIT---
# Layout Manager Selection Guide
Use this skill when the main question is whether the editor should use TextKit 1 or TextKit 2.
## When to Use
- You are choosing between `NSLayoutManager` and `NSTextLayoutManager`.- You are evaluating migration risk or performance tradeoffs.- You need a recommendation tied to feature requirements.
## Quick Decision
```Need glyph-level access? YES → NSLayoutManager (TextKit 1)
Need multi-page/multi-column layout? YES → NSLayoutManager (TextKit 1)
Need text tables (NSTextTable)? YES → NSLayoutManager (TextKit 1)
Need Writing Tools inline experience? YES → NSTextLayoutManager (TextKit 2)
Document > 10K lines and performance-critical? → Read the Performance Evidence section carefully
Building new app, iOS 16+? → Start with TextKit 2 (default). Fall back to TK1 only if needed.
Maintaining legacy app? → Stay on TextKit 1 unless you have a specific reason to migrate.```
## Core Guidance
## The Two Layout Managers
| Aspect | NSLayoutManager (TK1) | NSTextLayoutManager (TK2) ||--------|----------------------|--------------------------|| **Available** | iOS 7+ / macOS 10.0+ | iOS 15+ / macOS 12+ || **Layout model** | Contiguous (optional non-contiguous) | Always non-contiguous (viewport) || **Abstraction** | Glyph-based | Element/fragment-based || **Text containers** | Multiple | Single only || **Performance model** | O(document) or O(visible) | O(viewport) theoretical || **International text** | Manual glyph handling | Correct by design || **Writing Tools** | Panel only | Full inline || **Custom rendering** | Subclass + drawGlyphs | Subclass NSTextLayoutFragment || **Overlay styling** | Temporary attributes | Rendering attributes || **Printing** | Full support | Limited (iOS 18+) |
## Performance Evidence
### Apple's Claims (WWDC21)
> *"TextKit 2 is extremely fast for an incredibly wide range of scenarios, from quickly rendering labels that are only a few lines each to laying out documents that are hundreds of megabytes being scrolled through at interactive rates."*
### Developer Experience (Real-World)
**ChimeHQ TextViewBenchmark (macOS 14 beta):**> *"TextKit 1 is extremely fast and TextKit 2 actually even a small amount faster."*
For comparable document sizes on recent macOS, TextKit 2 has reached parity or slightly better.
**Large document scrolling (Apple Developer Forums, multiple reports):**- TextKit 2 scrolling performance degrades above ~3,000 lines- 10K+ lines described as "an absolute nightmare" with TextKit 2- Switching to TextKit 1 restored smooth scrolling with 1 million characters- These reports are primarily from iOS 16/17 era; improvements in each release
**Memory usage (developer measurement):**- ~0.5 GB with TextKit 1 custom labels- ~1.2 GB with TextKit 2 for same content- TextKit 2's immutable object model (NSTextLayoutFragment, NSTextLineFragment) has overhead
**Apple's own apps:**- Pages, Xcode, Notes: still use TextKit 1 (as of reports through 2025)- TextEdit: uses TextKit 2 but falls back for tables, page layout, printing
### Short Text (Labels, Chat Bubbles)
TextKit 2 wins here:- No performance penalty for viewport management on short text- Correctness benefits for international text- Modern API with rendering attributes
### Large Documents (10K+ lines)
Mixed results:- **Theoretical advantage:** Viewport layout should scale to any document size- **Practical issues:** Scroll bar jiggle (estimated heights), jump-to-position inaccuracy, height estimation instability- **Improving per OS release:** macOS 14+ shows near-parity in benchmarks- **Recommendation:** Test on your target OS version with your actual content
### Line Counting
Both systems struggle with this:- **TextKit 1:** `numberOfGlyphs` + enumeration. Requires full layout with `allowsNonContiguousLayout = false`, or approximate with non-contiguous.- **TextKit 2:** Must enumerate ALL layout fragments with `.ensuresLayout`. Defeats viewport optimization.
**For either system:** Consider maintaining a separate line count (incremental update on edit) rather than querying the layout system.
## When TextKit 1 Is the Right Choice
1. **Glyph-level access** — Custom glyph substitution, glyph inspection, typography tools2. **Multi-page/multi-column layout** — NSTextLayoutManager supports only one container3. **Text tables** — NSTextTable/NSTextTableBlock are TextKit 1 only4. **Proven reliability for large documents** — Decades of battle-testing5. **Need printing** — Better pagination control (though TextKit 2 printing improving)6. **Custom NSLayoutManager subclass** — Significant investment in `drawGlyphs`, `drawBackground`, delegate methods7. **`shouldGenerateGlyphs` delegate** — No TextKit 2 equivalent8. **Exact document height required** — TextKit 1 contiguous layout gives exact height; TextKit 2 estimates9. **Scroll bar accuracy critical** — TextKit 2's estimated heights cause scroll bar instability
## When TextKit 2 Is the Right Choice
1. **New iOS 16+ app** — It's the default; fighting it adds complexity2. **Writing Tools (full inline)** — Requires TextKit 23. **International text correctness** — Arabic, Devanagari, CJK handled correctly without glyph assumptions4. **Custom rendering via fragments** — Cleaner API than drawGlyphs subclassing5. **Short text (labels, chat bubbles)** — No downside, cleaner API6. **Viewport-based display of large content** — When you don't need exact document metrics7. **Custom text elements** — NSTextContentManager subclass for non-attributed-string backends
## Migration Decision Framework
### Don't Migrate If:
- App works well with TextKit 1- You rely on glyph APIs extensively- You need multi-container layout- No specific TextKit 2 feature is required- Target OS includes iOS 15 or earlier
### Consider Migrating If:
- Users need Writing Tools inline experience- International text rendering issues in TextKit 1- Building new text features from scratch- Want viewport performance for very large documents- Need rendering attributes (cleaner than temporary attributes)
### Migration Strategy
1. **Check `textLayoutManager` first** — write all new code to check TextKit 2 availability2. **Dual code paths** — support both TK1 and TK2 during transition3. **Test fallback** — ensure your app handles fallback gracefully4. **Migrate incrementally** — one feature at a time, not big bang
```swift// Dual code path patternif let textLayoutManager = textView.textLayoutManager { // TextKit 2 path textLayoutManager.enumerateTextLayoutFragments(from: nil, options: [.ensuresLayout]) { fragment in // ... return true }} else { // TextKit 1 fallback let layoutManager = textView.layoutManager! layoutManager.ensureLayout(for: textView.textContainer) // ...}```
## Common Pitfalls
1. **Migrating without a reason** — TextKit 1 works. Don't fix what isn't broken.2. **Assuming TextKit 2 is always faster** — Real-world performance depends on document size, OS version, and use case.3. **Not testing on target OS** — TextKit 2 performance improves each release. Test on YOUR minimum deployment target.4. **Full-document `ensureLayout` in TextKit 2** — Defeats viewport optimization. O(document_size).5. **Expecting exact scroll metrics from TextKit 2** — Estimated heights cause scroll bar instability. If exact metrics matter, use TextKit 1.
## Related Skills
- Use `/skill apple-text-textkit1-ref` or `/skill apple-text-textkit2-ref` after you know which stack you need.- Use `/skill apple-text-fallback-triggers` when compatibility mode is driving the decision.- Use `/skill apple-text-views` when the real question is control choice, not layout-manager internals.