Forked from unnamedd/MacEditorTextView.swift
Last active
April 14, 2025 01:02
Revisions
-
antingle revised this gist
May 4, 2022 . No changes.There are no files selected for viewing
-
antingle revised this gist
May 4, 2022 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ // // CustomMacTextView.swift // // MacEditorv2 Created by Marc Maset - 2021 // Changes inspired from MacEditorTextView by Thiago Holanda // // Modified by Anthony Ingle - 2022 @@ -115,7 +115,7 @@ fileprivate class PlaceholderNSTextView: NSTextView { didSet { var attributes = [NSAttributedString.Key: AnyObject]() attributes[.font] = font attributes[.foregroundColor] = NSColor.gray let captionAttributedString = NSAttributedString(string: placeholderText ?? "", attributes: attributes) placeholderAttributedString = captionAttributedString } -
antingle revised this gist
May 4, 2022 . 1 changed file with 20 additions and 16 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,7 +1,7 @@ // // CustomMacTextView.swift // // Foundation by Marc Maset - 2021 // Changes inspired from MacEditorTextView by Thiago Holanda // // Modified by Anthony Ingle - 2022 @@ -13,6 +13,7 @@ struct CustomMacTextView: NSViewRepresentable { var placeholderText: String? @Binding var text: String @Binding var shouldMoveCursorToEnd: Bool var font: NSFont = .systemFont(ofSize: 14, weight: .regular) var onSubmit : () -> Void = {} @@ -24,24 +25,34 @@ struct CustomMacTextView: NSViewRepresentable { } func makeNSView(context: Context) -> NSScrollView { let scrollView = PlaceholderNSTextView.scrollableTextView() guard let textView = scrollView.documentView as? PlaceholderNSTextView else { return scrollView } textView.delegate = context.coordinator textView.string = text textView.drawsBackground = false textView.font = font textView.allowsUndo = true textView.placeholderText = placeholderText scrollView.hasVerticalScroller = false return scrollView } func updateNSView(_ view: NSScrollView, context: Context) { guard let textView = view.documentView as? NSTextView else { return } // the range is reset when updating the string of the textView // so this will set it back to where it was previously let currentRange = textView.selectedRange() textView.string = text textView.setSelectedRange(currentRange) } } @@ -51,11 +62,11 @@ extension CustomMacTextView { class Coordinator: NSObject, NSTextViewDelegate { var parent: CustomMacTextView init(_ parent: CustomMacTextView) { self.parent = parent } func textDidBeginEditing(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return @@ -70,9 +81,8 @@ extension CustomMacTextView { return } self.parent.onTextChange(textView.string) self.parent.text = textView.string } func textDidEndEditing(_ notification: Notification) { @@ -84,23 +94,17 @@ extension CustomMacTextView { self.parent.onSubmit() } // handles commands func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { if (commandSelector == #selector(NSResponder.insertNewline(_:))) { // Do something when ENTER key pressed self.parent.onSubmit() return true } // return true if the action was handled; otherwise false return false } } } @@ -111,7 +115,7 @@ fileprivate class PlaceholderNSTextView: NSTextView { didSet { var attributes = [NSAttributedString.Key: AnyObject]() attributes[.font] = font attributes[.foregroundColor] = NSColor.darkGray let captionAttributedString = NSAttributedString(string: placeholderText ?? "", attributes: attributes) placeholderAttributedString = captionAttributedString } -
antingle revised this gist
Apr 30, 2022 . 2 changed files with 119 additions and 257 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,119 @@ // // CustomMacTextView.swift // // MacEditorv2 Created by Marc Maset - 2021 // Changes inspired from MacEditorTextView by Thiago Holanda // // Modified by Anthony Ingle - 2022 // import SwiftUI struct CustomMacTextView: NSViewRepresentable { var placeholderText: String? @Binding var text: String var font: NSFont = .systemFont(ofSize: 14, weight: .regular) var onSubmit : () -> Void = {} var onTextChange : (String) -> Void = { _ in } var onEditingChanged: () -> Void = {} func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> NSScrollView { let theTextView = PlaceholderNSTextView.scrollableTextView() let textView = (theTextView.documentView as! PlaceholderNSTextView) textView.delegate = context.coordinator textView.string = text textView.drawsBackground = false textView.font = font textView.placeholderText = placeholderText theTextView.hasVerticalScroller = false return theTextView } func updateNSView(_ view: NSScrollView, context: Context) { guard let textView = view.documentView as? NSTextView else { return } textView.string = text } } extension CustomMacTextView { class Coordinator: NSObject, NSTextViewDelegate { var parent: CustomMacTextView var affectedCharRange: NSRange? init(_ parent: CustomMacTextView) { self.parent = parent } func textDidBeginEditing(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } self.parent.text = textView.string self.parent.onEditingChanged() } func textDidChange(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } // Update text self.parent.text = textView.string self.parent.onTextChange(textView.string) } func textDidEndEditing(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } self.parent.text = textView.string self.parent.onSubmit() } func textView(_ textView: NSTextView, shouldChangeTextIn affectedCharRange: NSRange, replacementString: String?) -> Bool { return true } // handles commands func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { if (commandSelector == #selector(NSResponder.insertNewline(_:))) { // Do something against ENTER key self.parent.onSubmit() return true } // return true if the action was handled; otherwise false return false } } } // for setting a proper placeholder text on an NSTextView fileprivate class PlaceholderNSTextView: NSTextView { @objc private var placeholderAttributedString: NSAttributedString? var placeholderText: String? { didSet { var attributes = [NSAttributedString.Key: AnyObject]() attributes[.font] = font attributes[.foregroundColor] = NSColor.gray let captionAttributedString = NSAttributedString(string: placeholderText ?? "", attributes: attributes) placeholderAttributedString = captionAttributedString } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,257 +0,0 @@ -
antingle revised this gist
Apr 30, 2022 . 2 changed files with 51 additions and 57 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,21 +1,28 @@ /** * MacEditorTextView * Copyright (c) Thiago Holanda 2020-2021 * https://twitter.com/tholanda * * MIT license * * Edited by Anthony Ingle * - Added placeholder text * - onSubmit is called when enter/return key is pressed * - Background is not drawn (current behavior of TextField in SwiftUI) */ import Combine import SwiftUI struct MacEditorTextView: NSViewRepresentable { @Binding var text: String var placeholderText: String? var isEditable: Bool = true var font: NSFont? = .systemFont(ofSize: 14, weight: .regular) var onEditingChanged: () -> Void = {} var onSubmit : () -> Void = {} var onTextChange : (String) -> Void = { _ in } func makeCoordinator() -> Coordinator { @@ -26,7 +33,8 @@ struct MacEditorTextView: NSViewRepresentable { let textView = CustomTextView( text: text, isEditable: isEditable, font: font, placeholderText: placeholderText ) textView.delegate = context.coordinator @@ -94,6 +102,7 @@ extension MacEditorTextView { self.parent.text = textView.string self.selectedRanges = textView.selectedRanges self.parent.onTextChange(textView.string) } func textDidEndEditing(_ notification: Notification) { @@ -102,8 +111,21 @@ extension MacEditorTextView { } self.parent.text = textView.string self.parent.onSubmit() } // handles commands func textView(_ textView: NSTextView, doCommandBy commandSelector: Selector) -> Bool { if (commandSelector == #selector(NSResponder.insertNewline(_:))) { // Do something when ENTER key pressed self.parent.onSubmit() return true } // return true if the action was handled; otherwise false return false } } } @@ -112,6 +134,7 @@ extension MacEditorTextView { final class CustomTextView: NSView { private var isEditable: Bool private var font: NSFont? private var placeholderText: String? weak var delegate: NSTextViewDelegate? @@ -133,9 +156,9 @@ final class CustomTextView: NSView { private lazy var scrollView: NSScrollView = { let scrollView = NSScrollView() scrollView.drawsBackground = false // set this to true to enable background colors/materials scrollView.borderType = .noBorder scrollView.hasVerticalScroller = false scrollView.hasHorizontalRuler = false scrollView.autoresizingMask = [.width, .height] scrollView.translatesAutoresizingMaskIntoConstraints = false @@ -161,12 +184,10 @@ final class CustomTextView: NSView { layoutManager.addTextContainer(textContainer) let textView = PlaceholderNSTextView(frame: .zero, textContainer: textContainer) textView.autoresizingMask = .width textView.delegate = self.delegate textView.drawsBackground = false textView.font = self.font textView.isEditable = self.isEditable textView.isHorizontallyResizable = false @@ -175,15 +196,17 @@ final class CustomTextView: NSView { textView.minSize = NSSize(width: 0, height: contentSize.height) textView.textColor = NSColor.labelColor textView.allowsUndo = true textView.placeholderText = self.placeholderText return textView }() // MARK: - Init init(text: String, isEditable: Bool, font: NSFont?, placeholderText: String?) { self.font = font self.isEditable = isEditable self.text = text self.placeholderText = placeholderText super.init(frame: .zero) } @@ -217,4 +240,18 @@ final class CustomTextView: NSView { func setupTextView() { scrollView.documentView = textView } } // for setting a proper placeholder text on an NSTextView class PlaceholderNSTextView: NSTextView { @objc private var placeholderAttributedString: NSAttributedString? var placeholderText: String? { didSet { var attributes = [NSAttributedString.Key: AnyObject]() attributes[.font] = font attributes[.foregroundColor] = NSColor.gray let captionAttributedString = NSAttributedString(string: placeholderText ?? "", attributes: attributes) placeholderAttributedString = captionAttributedString } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,43 +0,0 @@ -
unnamedd revised this gist
Feb 10, 2021 . 2 changed files with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ /** * MacEditorTextView * Copyright (c) Thiago Holanda 2020-2021 * https://twitter.com/tholanda * * MIT license This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,6 +1,6 @@ /** * MacEditorTextView * Copyright (c) Thiago Holanda 2020-2021 * https://twitter.com/tholanda * * MIT license -
unnamedd revised this gist
Dec 18, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -174,6 +174,7 @@ final class CustomTextView: NSView { textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) textView.minSize = NSSize(width: 0, height: contentSize.height) textView.textColor = NSColor.labelColor textView.allowsUndo = true return textView }() -
unnamedd revised this gist
May 26, 2020 . 2 changed files with 60 additions and 30 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,27 +1,33 @@ /** * MacEditorTextView * Copyright (c) Thiago Holanda 2020 * https://twitter.com/tholanda * * MIT license */ import Combine import SwiftUI struct MacEditorTextView: NSViewRepresentable { @Binding var text: String var isEditable: Bool = true var font: NSFont? = .systemFont(ofSize: 14, weight: .regular) var onEditingChanged: () -> Void = {} var onCommit : () -> Void = {} var onTextChange : (String) -> Void = { _ in } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> CustomTextView { let textView = CustomTextView( text: text, isEditable: isEditable, font: font ) textView.delegate = context.coordinator return textView @@ -33,23 +39,37 @@ struct MacEditorTextView: NSViewRepresentable { } } // MARK: - Preview #if DEBUG struct MacEditorTextView_Previews: PreviewProvider { static var previews: some View { Group { MacEditorTextView( text: .constant("{ \n planets { \n name \n }\n}"), isEditable: true, font: .userFixedPitchFont(ofSize: 14) ) .environment(\.colorScheme, .dark) .previewDisplayName("Dark Mode") MacEditorTextView( text: .constant("{ \n planets { \n name \n }\n}"), isEditable: false ) .environment(\.colorScheme, .light) .previewDisplayName("Light Mode") } } } #endif // MARK: - Coordinator extension MacEditorTextView { class Coordinator: NSObject, NSTextViewDelegate { var parent: MacEditorTextView var selectedRanges: [NSValue] = [] @@ -87,9 +107,11 @@ extension MacEditorTextView { } } // MARK: - CustomTextView final class CustomTextView: NSView { private var isEditable: Bool private var font: NSFont? weak var delegate: NSTextViewDelegate? @@ -152,16 +174,16 @@ final class CustomTextView: NSView { textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) textView.minSize = NSSize(width: 0, height: contentSize.height) textView.textColor = NSColor.labelColor return textView }() // MARK: - Init init(text: String, isEditable: Bool, font: NSFont?) { self.font = font self.isEditable = isEditable self.text = text super.init(frame: .zero) } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,17 +15,25 @@ struct ContentQueryView: View { @State private var responseJSONText = "{ \"name\": \"Earth\"}" var body: some View { let queryTextView = MacEditorTextView( text: $queryText, isEditable: false, font: .systemFont(ofSize: 14, weight: .regular) ) .frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) let responseTextView = MacEditorTextView( text: $responseJSONText, isEditable: false, font: .userFixedPitchFont(ofSize: 14) ) .frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) return HSplitView { queryTextView -
unnamedd revised this gist
Jan 30, 2020 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -39,11 +39,11 @@ struct MacEditorTextView_Previews: PreviewProvider { Group { MacEditorTextView(text: .constant("{ \n planets { \n name \n }\n}")) .environment(\.colorScheme, .dark) .previewDisplayName("Dark Mode") MacEditorTextView(text: .constant("{ \n planets { \n name \n }\n}")) .environment(\.colorScheme, .light) .previewDisplayName("Light Mode") } } } -
unnamedd revised this gist
Jan 21, 2020 . 2 changed files with 8 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -34,27 +34,27 @@ struct MacEditorTextView: NSViewRepresentable { } #if DEBUG struct MacEditorTextView_Previews: PreviewProvider { static var previews: some View { Group { MacEditorTextView(text: .constant("{ \n planets { \n name \n }\n}")) .environment(\.colorScheme, .dark) .previewDisplayName("Light Mode") MacEditorTextView(text: .constant("{ \n planets { \n name \n }\n}")) .environment(\.colorScheme, .light) .previewDisplayName("Dark Mode") } } } #endif extension MacEditorTextView { class Coordinator: NSObject, NSTextViewDelegate { var parent: MacEditorTextView var selectedRanges: [NSValue] = [] init(_ parent: MacEditorTextView) { self.parent = parent } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,13 +15,13 @@ struct ContentQueryView: View { @State private var responseJSONText = "{ \"name\": \"Earth\"}" var body: some View { let queryTextView = MacEditorTextView(text: $queryText) .frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) let responseTextView = MacEditorTextView(text: $responseJSONText) .frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, -
unnamedd revised this gist
Jan 21, 2020 . 2 changed files with 43 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,35 @@ /** * MacEditorTextView * Copyright (c) Thiago Holanda 2020 * https://twitter.com/tholanda * * MIT license */ import SwiftUI import Combine struct ContentQueryView: View { @State private var queryText = "{ \n planets { \n name \n }\n}" @State private var responseJSONText = "{ \"name\": \"Earth\"}" var body: some View { let queryTextView = EditorTextView(text: $queryText) .frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) let responseTextView = EditorTextView(text: $responseJSONText) .frame(minWidth: 300, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity) return HSplitView { queryTextView responseTextView } } } This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,12 +1,15 @@ /** * MacEditorTextView * Copyright (c) Thiago Holanda 2020 * https://twitter.com/tholanda * * MIT license */ import Combine import SwiftUI struct MacEditorTextView: NSViewRepresentable { @Binding var text: String var onEditingChanged : () -> Void = {} -
unnamedd revised this gist
Jan 21, 2020 . 1 changed file with 3 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -9,8 +9,9 @@ import SwiftUI struct EditorTextView: NSViewRepresentable { @Binding var text: String var onEditingChanged : () -> Void = {} var onCommit : () -> Void = {} var onTextChange : (String) -> Void = { _ in } func makeCoordinator() -> Coordinator { Coordinator(self) -
unnamedd revised this gist
Jul 23, 2019 . 1 changed file with 22 additions and 10 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -11,7 +11,7 @@ struct EditorTextView: NSViewRepresentable { var onEditingChanged: () -> Void = {} var onCommit: () -> Void = {} func makeCoordinator() -> Coordinator { Coordinator(self) } @@ -25,6 +25,7 @@ struct EditorTextView: NSViewRepresentable { func updateNSView(_ view: CustomTextView, context: Context) { view.text = text view.selectedRanges = context.coordinator.selectedRanges } } @@ -47,6 +48,7 @@ struct EditorTextView_Previews: PreviewProvider { extension EditorTextView { class Coordinator: NSObject, NSTextViewDelegate { var parent: EditorTextView var selectedRanges: [NSValue] = [] init(_ parent: EditorTextView) { self.parent = parent @@ -67,6 +69,7 @@ extension EditorTextView { } self.parent.text = textView.string self.selectedRanges = textView.selectedRanges } func textDidEndEditing(_ notification: Notification) { @@ -92,6 +95,16 @@ final class CustomTextView: NSView { } } var selectedRanges: [NSValue] = [] { didSet { guard selectedRanges.count > 0 else { return } textView.selectedRanges = selectedRanges } } private lazy var scrollView: NSScrollView = { let scrollView = NSScrollView() scrollView.drawsBackground = true @@ -124,26 +137,25 @@ final class CustomTextView: NSView { let textView = NSTextView(frame: .zero, textContainer: textContainer) textView.autoresizingMask = .width textView.backgroundColor = NSColor.textBackgroundColor textView.delegate = self.delegate textView.drawsBackground = true textView.font = self.font textView.isEditable = self.isEditable textView.isHorizontallyResizable = false textView.isVerticallyResizable = true textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) textView.minSize = NSSize(width: 0, height: contentSize.height) textView.textColor = NSColor.labelColor return textView }() // MARK: - Init init(text: String, isEditable: Bool = true, font: NSFont = NSFont.systemFont(ofSize: 32, weight: .ultraLight)) { self.font = font self.isEditable = isEditable self.text = text super.init(frame: .zero) -
unnamedd created this gist
Jul 23, 2019 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,181 @@ // // Created by Thiago Holanda on 22.07.19. // Copyright © 2019 unnamedd codes. All rights reserved. // import Combine import SwiftUI struct EditorTextView: NSViewRepresentable { @Binding var text: String var onEditingChanged: () -> Void = {} var onCommit: () -> Void = {} func makeCoordinator() -> Coordinator { Coordinator(self) } func makeNSView(context: Context) -> CustomTextView { let textView = CustomTextView(text: self.text) textView.delegate = context.coordinator return textView } func updateNSView(_ view: CustomTextView, context: Context) { view.text = text } } #if DEBUG struct EditorTextView_Previews: PreviewProvider { static var previews: some View { Group { EditorTextView(text: .constant("{ \n planets { \n name \n }\n}")) .environment(\.colorScheme, .dark) .previewDisplayName("Light Mode") EditorTextView(text: .constant("{ \n planets { \n name \n }\n}")) .environment(\.colorScheme, .light) .previewDisplayName("Dark Mode") } } } #endif extension EditorTextView { class Coordinator: NSObject, NSTextViewDelegate { var parent: EditorTextView init(_ parent: EditorTextView) { self.parent = parent } func textDidBeginEditing(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } self.parent.text = textView.string self.parent.onEditingChanged() } func textDidChange(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } self.parent.text = textView.string } func textDidEndEditing(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } self.parent.text = textView.string self.parent.onCommit() } } } final class CustomTextView: NSView { private var isEditable: Bool private var font: NSFont weak var delegate: NSTextViewDelegate? var text: String { didSet { textView.string = text } } private lazy var scrollView: NSScrollView = { let scrollView = NSScrollView() scrollView.drawsBackground = true scrollView.borderType = .noBorder scrollView.hasVerticalScroller = true scrollView.hasHorizontalRuler = false scrollView.autoresizingMask = [.width, .height] scrollView.translatesAutoresizingMaskIntoConstraints = false return scrollView }() private lazy var textView: NSTextView = { let contentSize = scrollView.contentSize let textStorage = NSTextStorage() let layoutManager = NSLayoutManager() textStorage.addLayoutManager(layoutManager) let textContainer = NSTextContainer(containerSize: scrollView.frame.size) textContainer.widthTracksTextView = true textContainer.containerSize = NSSize( width: contentSize.width, height: CGFloat.greatestFiniteMagnitude ) layoutManager.addTextContainer(textContainer) let textView = NSTextView(frame: .zero, textContainer: textContainer) textView.minSize = NSSize(width: 0, height: contentSize.height) textView.maxSize = NSSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) textView.isVerticallyResizable = true textView.isHorizontallyResizable = false textView.autoresizingMask = .width textView.drawsBackground = true textView.backgroundColor = NSColor.textBackgroundColor textView.textColor = NSColor.labelColor textView.font = self.font textView.isEditable = self.isEditable textView.delegate = self.delegate return textView }() // MARK: - Init init(text: String, isEditable: Bool = true, font: NSFont = NSFont.systemFont(ofSize: 32, weight: .ultraLight)) { self.isEditable = isEditable self.font = font self.text = text super.init(frame: .zero) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Life cycle override func viewWillDraw() { super.viewWillDraw() setupScrollViewConstraints() setupTextView() } func setupScrollViewConstraints() { scrollView.translatesAutoresizingMaskIntoConstraints = false addSubview(scrollView) NSLayoutConstraint.activate([ scrollView.topAnchor.constraint(equalTo: topAnchor), scrollView.trailingAnchor.constraint(equalTo: trailingAnchor), scrollView.bottomAnchor.constraint(equalTo: bottomAnchor), scrollView.leadingAnchor.constraint(equalTo: leadingAnchor) ]) } func setupTextView() { scrollView.documentView = textView } }