Skip to content

Instantly share code, notes, and snippets.

@niw
Last active May 12, 2025 23:02
Show Gist options
  • Save niw/6f44f539709fc8a03aa86d09503e7efe to your computer and use it in GitHub Desktop.
Save niw/6f44f539709fc8a03aa86d09503e7efe to your computer and use it in GitHub Desktop.

Revisions

  1. niw revised this gist May 12, 2025. No changes.
  2. niw created this gist May 5, 2025.
    85 changes: 85 additions & 0 deletions MainView.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,85 @@
    import AppKit
    import SwiftUI

    public struct TextView: NSViewRepresentable {
    @Binding
    var text: String
    var font: NSFont?

    public init(
    text: Binding<String>
    ) {
    self._text = text
    }

    public class Coordinator: NSObject, NSTextViewDelegate {
    var parent: TextView

    init(_ parent: TextView) {
    self.parent = parent
    }

    public func textDidChange(_ notification: Notification) {
    guard let textView = notification.object as? NSTextView else {
    return
    }
    parent.text = textView.string
    }
    }

    public func makeCoordinator() -> Coordinator {
    Coordinator(self)
    }

    public func makeNSView(context: Context) -> NSScrollView {
    let nsView = NSTextView.scrollableTextView()
    let textView = nsView.documentView as! NSTextView
    textView.delegate = context.coordinator
    textView.textContainerInset = .init(width: 10.0, height: 10.0)
    nsView.documentView = textView
    return nsView
    }

    public func updateNSView(_ nsView: NSScrollView, context: Context) {
    (nsView.documentView as! NSTextView).string = text
    }

    public func sizeThatFits(_ proposal: ProposedViewSize, nsView: NSScrollView, context: Context) -> CGSize? {
    let textView = nsView.documentView as! NSTextView
    guard let textLayoutManager = textView.textLayoutManager else {
    return nil
    }
    let rect = textLayoutManager.usageBoundsForTextContainer
    print(rect)
    let width = proposal.width ?? 0.0
    let height = rect.height + textView.textContainerInset.height * 2
    return CGSize(width: width, height: height)
    }
    }

    struct MainView: View {
    @State
    private var text: String = "This is sample text.\nHello, World!"

    var body: some View {
    VStack {
    ScrollView {
    LazyVStack {
    ForEach(Array(0..<100), id: \.self) { id in
    Text("\(id)")
    }
    }
    }
    .border(.blue)

    TextView(text: $text)
    .border(.red)
    .fixedSize(horizontal: false, vertical: true)
    }
    .scenePadding()
    }
    }

    #Preview {
    MainView()
    }