Search code examples
swiftmacoscocoaswift5nstextview

How to resize a NSTextView automatically as per its content?


I am making an app where a user can click anywhere on the window and a NSTextView is added programmatically at the mouse location. I have got it working with the below code but I want this NSTextView to horizontally expand until it reaches the edge of the screen and then grow vertically. It currently has a fixed width and when I add more characters, the text view grows vertically (as expected) but I also want it to grow horizontally. How can I achieve this?

I have tried setting isHorizontallyResizable and isVerticallyResizable to true but this doesn't work. After researching for a while, I came across this https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextStorageLayer/Tasks/TrackingSize.html but this didn't work for me either.

Code in my ViewController to add the NSTextView to its view:

private func addText(at point: NSPoint) {     
    let textView = MyTextView(frame: NSRect(origin: point, size: CGSize(width: 150.0, height: 40.0)))
    view.addSubview(textView)       
}

And, MyTextView class looks like below:

class MyTextView: NSTextView {

    override func viewWillDraw() {
        
        isHorizontallyResizable = true
        isVerticallyResizable = true
        
        isRichText = false
    }
}

I have also seen this answer https://stackoverflow.com/a/54228147/1385441 but I am not fully sure how to implement it. I have added this code snippet in MyTextView and used it like:

override func didChangeText() {
    frame.size = contentSize
}

However, I think I am using it incorrectly. Ergo, any help would be much appreciated.


Solution

  • I'm a bit puzzled, because you're adding NSTextView to a NSView which is part of the NSViewController and then you're talking about the screen width. Is this part of your Presentify - Screen Annotation application? If yes, you have a full screen overlay window and you can get the size from it (or from the view controller's view).

    view.bounds.size                // view controller's view size
    view.window?.frame.size         // window size
    

    If not and you really need to know the screen size, check the NSWindow & NSScreen.

    view.window?.screen?.frame.size // screen size
    

    Growing NSTextView

    There's no any window/view controller's view resizing behavior specified.

    import Cocoa
    
    class BorderedTextView: NSTextView {
        override func draw(_ dirtyRect: NSRect) {
            super.draw(dirtyRect)
            
            let path = NSBezierPath(rect: bounds)
            NSColor.red.setStroke()
            path.stroke()
        }
    }
    
    class ViewController: NSViewController {
        override func mouseUp(with event: NSEvent) {
            // Convert point to the view coordinates
            let point = view.convert(event.locationInWindow, from: nil)
            
            // Initial size
            let size = CGSize(width: 100, height: 25)
            
            // Maximum text view width
            let maxWidth = view.bounds.size.width - point.x             // <----
                    
            let textView = BorderedTextView(frame: NSRect(origin: point, size: size))
            textView.insertionPointColor = .orange
            textView.drawsBackground = false
            textView.textColor = .white
            textView.isRichText = false
            textView.allowsUndo = false
            textView.font = NSFont.systemFont(ofSize: 20.0)
            textView.isVerticallyResizable = true
            textView.isHorizontallyResizable = true
                    
            textView.textContainer?.widthTracksTextView = false
            textView.textContainer?.heightTracksTextView = false
            textView.textContainer?.size.width = maxWidth               // <----
            textView.maxSize = NSSize(width: maxWidth, height: 10000)   // <----
            
            view.addSubview(textView)
            
            view.window?.makeFirstResponder(textView)
        }
    }
    

    enter image description here