Skip to content
All essays
iOSMarch 27, 202511 min

Swift Text Kit: Advanced Text Processing

Process and display text with TextKit. Text layout, rendering, and text manipulation.

Ü
Ümit Uz
Mobile & Full Stack Developer

TextKit provides advanced text rendering and layout capabilities. Learn to create rich text experiences.

TextKit Setup

Basic Text Container

swift
import TextKit
import UIKit

class TextProcessor {
    func createTextContainer(size: CGSize) -> NSTextContainer {
        let container = NSTextContainer(size: size)
        container.lineFragmentPadding = 0
        container.maximumNumberOfLineFragments = 0
        return container
    }

    func createLayoutManager() -> NSLayoutManager {
        let layoutManager = NSLayoutManager()
        layoutManager.usesFontLeading = false
        return layoutManager
    }

    func createTextStorage() -> NSTextStorage {
        let textStorage = NSTextStorage(string: "")
        return textStorage
    }
}

Text Layout

Simple Text Layout

swift
class TextLayoutManager {
    func layoutText(_ text: String, in container: NSTextContainer) {
        let textStorage = NSTextStorage(string: text)
        let layoutManager = NSLayoutManager()

        layoutManager.addTextContainer(container)
        textStorage.addLayoutManager(layoutManager)

        let range = NSRange(location: 0, length: textStorage.length)
        layoutManager_glyphRange(for: container)
        layoutManager.setGlyphs(range)
    }
}

Attributed Text

swift
class AttributedTextManager {
    func createAttributedText() -> NSAttributedString {
        let text = "Hello, World!"

        let attributedString = NSMutableAttributedString(string: text)

        // Add attributes
        attributedString.addAttribute(
            .font,
            value: UIFont.boldSystemFont(ofSize: 24),
            range: NSRange(location: 0, length: 5)
        )

        attributedString.addAttribute(
            .foregroundColor,
            value: UIColor.blue,
            range: NSRange(location: 7, length: 5)
        )

        return attributedString
    }

    func createRichText() -> NSAttributedString {
        let text = """
        Welcome to Swift
        Advanced Text Processing
        """

        let attributedString = NSMutableAttributedString(string: text)

        // Apply different styles
        let styles: [(range: NSRange, color: UIColor, font: UIFont)] = [
            (
                range: NSRange(location: 0, length: 7),
                color: .blue,
                font: UIFont.boldSystemFont(ofSize: 20)
            ),
            (
                range: NSRange(location: 8, length: 27),
                color: .darkGray,
                font: UIFont.systemFont(ofSize: 16)
            )
        ]

        for style in styles {
            attributedString.addAttribute(.foregroundColor, value: style.color, range: style.range)
            attributedString.addAttribute(.font, value: style.font, range: style.range)
        }

        return attributedString
    }
}

Text Rendering

Render to View

swift
class TextRenderer {
    func renderTextToView(_ view: UIView, text: String) {
        view.backgroundColor = .white

        let textStorage = NSTextStorage(string: text)
        let layoutManager = NSLayoutManager()
        let container = NSTextContainer(size: view.bounds.size)

        layoutManager.addTextContainer(container)
        textStorage.addLayoutManager(layoutManager)

        // Render
        let graphicsContext = UIGraphicsGetCurrentContext()
        container.draw(at: .zero, in: graphicsContext)
    }
}

Text Analysis

Text Attributes

swift
class TextAnalyzer {
    func analyzeTextAttributes(_ attributedString: NSAttributedString) {
        let range = NSRange(location: 0, length: attributedString.length)

        // Enumerate attributes
        attributedString.enumerateAttributes(in: range) { attributes, range, _ in
            if let font = attributes[.font] as? UIFont {
                print("Font: \(font.fontName), size: \(font.pointSize)")
            }

            if let color = attributes[.foregroundColor] as? UIColor {
                print("Color: \(color)")
            }

            if let paragraphStyle = attributes[.paragraphStyle] as? NSParagraphStyle {
                print("Paragraph style: \(paragraphStyle)")
            }
        }
    }

    func extractTextAttributes(at position: Int, from attributedString: NSAttributedString) -> [NSAttributedString.Key: Any] {
        let range = NSRange(location: position, length: 1)
        var attributes: [NSAttributedString.Key: Any] = [:]

        attributedString.enumerateAttributes(in: range) { attrs, _, _ in
            for (key, value) in attrs {
                attributes[key] = value
            }
        }

        return attributes
    }
}

Text Editing

Text Storage Management

swift
class TextEditorManager: NSObject, NSTextStorageDelegate {
    var textStorage: NSTextStorage!
    var layoutManager: NSLayoutManager!
    var textContainer: NSTextContainer!

    func setupEditor(in view: UIView) {
        // Create text storage
        textStorage = NSTextStorage(string: "")
        textStorage.delegate = self

        // Create layout manager
        layoutManager = NSLayoutManager()
        textStorage.addLayoutManager(layoutManager)

        // Create text container
        textContainer = NSTextContainer(size: view.bounds.size)
        layoutManager.addTextContainer(textContainer)

        // Set up text view
        let textView = UITextView(frame: view.bounds, textContainer: textContainer, layoutManager: layoutManager)
        view.addSubview(textView)
    }

    func insertText(_ text: String, at range: NSRange) {
        textStorage.replaceCharacters(in: range, with: text)
    }

    func applyAttributes(_ attributes: [NSAttributedString.Key: Any], to range: NSRange) {
        textStorage.setAttributes(attributes, range: range)
    }
}

Text Calculation

Measure Text

swift
extension TextProcessor {
    func measureText(_ text: String, constrainedTo size: CGSize, font: UIFont) -> CGRect {
        let attributes: [NSAttributedString.Key: Any] = [.font: font]
        let attributedString = NSAttributedString(string: text, attributes: attributes)

        let framesetter = CTFramesetterCreateWithAttributedString(attributedString)
        let path = CGPath(rect: CGRect(origin: .zero, size: size), transform: nil)

        let bounds = CTFramesetterGetOptimizedBounds(framesetter, path, range: NSRange(location: 0, length: attributedString.length))

        return bounds
    }

    func calculateHeight(for text: String, width: CGFloat, font: UIFont) -> CGFloat {
        let size = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude)
        let boundingRect = measureText(text, constrainedTo: size, font: font)
        return ceil(boundingRect.height)
    }
}

Exclusion Paths

Text Around Shapes

swift
class ExclusionPathManager {
    func createExclusionPath(in container: NSTextContainer, around rect: CGRect) {
        let bezierPath = UIBezierPath(rect: rect)
        container.exclusionPaths = [bezierPath]
    }

    func createCircularExclusion(in container: NSTextContainer, center: CGPoint, radius: CGFloat) {
        let bezierPath = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: .pi * 2, clockwise: true)
        container.exclusionPaths = [bezierPath]
    }

    func createImageExclusion(in container: NSTextContainer, around image: UIImage) {
        // Create exclusion path around image
        let imageSize = image.size
        let imageRect = CGRect(
            origin: .zero,
            size: CGSize(width: imageSize.width + 20, height: imageSize.height + 20)
        )

        let bezierPath = UIBezierPath(roundedRect: imageRect, cornerRadius: 10)
        container.exclusionPaths = [bezierPath]
    }
}

Text Selection

Custom Selection

swift
class TextSelectionManager {
    func enableSelection(in view: UITextView) {
        view.isSelectable = true
        view.isEditable = false
        view.textContainer.exclusionPaths = []
    }

    func getSelectedText(from view: UITextView) -> String? {
        guard let range = view.selectedTextRange else {
            return nil
        }

        return view.text(in: range)
    }

    func selectText(in view: UITextView, range: NSRange) {
        view.selectedRange = range
    }
}

Best Practices

  1. 1Performance: Optimize for large texts
  2. 2Memory: Manage text storage efficiently
  3. 3Layout: Use appropriate layouts
  4. 4Attributes: Apply attributes efficiently
  5. 5Rendering: Render asynchronously when possible
  6. 6Accessibility: Support accessibility features
  7. 7Testing: Test with various text content
  8. 8Localization: Support right-to-left text

TextKit enables powerful text processing capabilities!

Related essays

Next essay
Swift AVFoundation: Video Processing