- 
            
      
        
      
    Star
      
          
          (185)
      
  
You must be signed in to star a gist 
- 
              
      
        
      
    Fork
      
          
          (13)
      
  
You must be signed in to fork a gist 
- 
      
- 
        Save unnamedd/6e8c3fbc806b8deb60fa65d6b9affab0 to your computer and use it in GitHub Desktop. 
| /** | |
| * MacEditorTextView | |
| * Copyright (c) Thiago Holanda 2020-2021 | |
| * https://bsky.app/profile/tholanda.com | |
| * | |
| * (the twitter account is now deleted, please, do not try to reach me there) | |
| * 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 | |
| } | |
| func updateNSView(_ view: CustomTextView, context: Context) { | |
| view.text = text | |
| view.selectedRanges = context.coordinator.selectedRanges | |
| } | |
| } | |
| // 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] = [] | |
| init(_ parent: MacEditorTextView) { | |
| 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 | |
| self.selectedRanges = textView.selectedRanges | |
| } | |
| func textDidEndEditing(_ notification: Notification) { | |
| guard let textView = notification.object as? NSTextView else { | |
| return | |
| } | |
| self.parent.text = textView.string | |
| self.parent.onCommit() | |
| } | |
| } | |
| } | |
| // MARK: - CustomTextView | |
| final class CustomTextView: NSView { | |
| private var isEditable: Bool | |
| private var font: NSFont? | |
| weak var delegate: NSTextViewDelegate? | |
| var text: String { | |
| didSet { | |
| textView.string = text | |
| } | |
| } | |
| var selectedRanges: [NSValue] = [] { | |
| didSet { | |
| guard selectedRanges.count > 0 else { | |
| return | |
| } | |
| textView.selectedRanges = selectedRanges | |
| } | |
| } | |
| 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.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 | |
| textView.allowsUndo = true | |
| return textView | |
| }() | |
| // MARK: - Init | |
| init(text: String, isEditable: Bool, font: NSFont?) { | |
| self.font = font | |
| self.isEditable = isEditable | |
| 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 | |
| } | |
| } | 
| /** | |
| * MacEditorTextView | |
| * Copyright (c) Thiago Holanda 2020-2021 | |
| * https://bsky.app/profile/tholanda.com | |
| * | |
| * (the twitter account is now deleted, please, do not try to reach me there) | |
| * 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 = 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 | |
| responseTextView | |
| } | |
| } | |
| } | 
@unnamedd  Find bar is not working, Could you please help fixing it.
I added below line of code for NSTextView.
textView.usesFindBar = true
textView.usesFindPanel = false
textView.isIncrementalSearchingEnabled = true
This lines works when i add textview using storyboard but not working when using this for SwiftUI
Hi @RizwanaDesai,
unfortunately I don't see why it is not working for you, in any case, I am working with a friend to create a package of this humble gist in order to make it better and easier to use.
I will give a check on the problem you've reported, but you can do that too, since the code is all exposed here and there's no hidden magic being done. I would of course highly appreciate if you post here a possible solution you may find (in case you find a fix before me).
Sure @unnamedd .
Thank you for this! I loved the simplicity of yours!
I have combined your implementation with the MacEditorTextView for the extra functions, along with a couple of other changes. One of them being the ability to add a placeholder text to the scrollview, and another being a working onSubmit function.
It is found here in case anyone can find the alterations beneficial.