Skip to content

Instantly share code, notes, and snippets.

@neknalb
Last active March 3, 2022 09:45
Show Gist options
  • Save neknalb/00398cd3e8b80172165ed744c7ac7da2 to your computer and use it in GitHub Desktop.
Save neknalb/00398cd3e8b80172165ed744c7ac7da2 to your computer and use it in GitHub Desktop.
A SwiftUI text view resizing horizontally according to its superview and vertically to fit its current text content – using NSTextView and intrinsicContentSize.
//
// TextViewWithIntrinsicContentSize.swift
//
// Created by Malte Blanken on 03.03.22.
//
// https://github.com/neknalb
//
// MIT license
//
import SwiftUI
public struct TextViewWithIntrinsicContentSize: NSViewRepresentable {
public class TextView: NSTextView {
public override var intrinsicContentSize: NSSize {
guard let layoutManager = textContainer?.layoutManager else {
return .zero
}
layoutManager.ensureLayout(for: textContainer!)
return layoutManager.usedRect(for: textContainer!).size
}
public override func resize(withOldSuperviewSize oldSize: NSSize) {
self.invalidateIntrinsicContentSize()
return super.resize(withOldSuperviewSize: oldSize)
}
}
public func makeNSView(context: Context) -> TextView {
let layoutManager = NSLayoutManager()
// Following text sample from <https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/TextSystemArchitecture/ArchitectureOverview.html>.
let textStorage = NSTextStorage(string: """
The Cocoa text system is abstracted into a set of classes that interact to provide all of the text-handling features of the system. The classes represent specific functional areas with well-defined interfaces that enable application programs to modify the behavior of the system or even to replace parts with custom subclasses. The Cocoa text system is designed so that you don’t need to learn about or interact with more of the system than is necessary to meet the needs of your application.
For most developers, the general-purpose programmatic interface of the NSTextView class is all you need to learn. NSTextView provides the user interface to the text system. See NSTextView Class Reference for detailed information about NSTextView.
If you need more flexible, programmatic access to the text, you’ll need to learn about the storage layer and the NSTextStorage class. And, of course, to access all the available features, you can interact with any of the classes that support the text system.
""")
let textContainer = NSTextContainer()
textStorage.addLayoutManager(layoutManager)
textContainer.widthTracksTextView = true
layoutManager.addTextContainer(textContainer)
let textView = TextView(frame: .zero, textContainer: textContainer)
textView.autoresizingMask = [.width, .height]
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
// Hint: Setting the textView.isHorizontallyResizable and/or .isVerticallyResizable is not helpfull.
// Hint: Setting the textView.maxSize and/or .minSize seems to have no effect.
textView.isEditable = true
textView.isSelectable = true
textView.allowsUndo = true
textView.drawsBackground = true
textView.backgroundColor = NSColor.textBackgroundColor
textView.font = NSFont.preferredFont(forTextStyle: .body)
textView.textColor = NSColor.textColor
textView.delegate = context.coordinator
return textView
}
public func updateNSView(_ textView: TextView, context: Context) {
textView.invalidateIntrinsicContentSize()
}
public func makeCoordinator() -> Coordinator {
return Coordinator(self)
}
public class Coordinator: NSObject, NSTextViewDelegate {
var parent: TextViewWithIntrinsicContentSize
init(_ parent: TextViewWithIntrinsicContentSize) {
self.parent = parent
}
public func textDidChange(_ notification: Notification) {
if let textView = notification.object as? NSTextView {
textView.invalidateIntrinsicContentSize()
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment