Scrollable NSTextField in alert message

Viewed 244

I'm writing a routine in swift (Xcode 11.6) that converts a text in rtf-format into a structured format under macOS.

For entering the plain text I try to define a text field which is scrollable in an alert message and have set up the following routine. Unfortunately the alert message has the right size but doesn't show the text field "originaltext". It works if I define the "originaltext" as NSTextView(_ frame:) and add it instead of scrollView as accessoryView to the alert message, but then of course it is not scrollable.

What do I do wrong? Thanks very much for your support

private func readNewText() {
        
        let abfrageNeuerText = NSAlert()
        abfrageNeuerText.alertStyle = .informational
        abfrageNeuerText.messageText = "Input new text"
        abfrageNeuerText.informativeText = ""
        abfrageNeuerText.addButton(withTitle: "Convert")
        abfrageNeuerText.addButton(withTitle: "Cancel")
        
        let scrollView = NSScrollView(frame: NSRect(x:10, y:10, width: 500, height: 700))
        scrollView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.borderType = .noBorder
        scrollView.backgroundColor = NSColor.white
        scrollView.hasVerticalScroller = true
        
        // Initial clip view
        let clipView = NSClipView()
        clipView.translatesAutoresizingMaskIntoConstraints = false
        scrollView.contentView = clipView
        scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .left, relatedBy: .equal, toItem: scrollView, attribute: .left, multiplier: 1.0, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .top, relatedBy: .equal, toItem: scrollView, attribute: .top, multiplier: 1.0, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .right, relatedBy: .equal, toItem: scrollView, attribute: .right, multiplier: 1.0, constant: 0))
        scrollView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .bottom, relatedBy: .equal, toItem: scrollView, attribute: .bottom, multiplier: 1.0, constant: 0))

        // Initial document view
        let originaltext = NSTextView()
        originaltext.translatesAutoresizingMaskIntoConstraints = false
        scrollView.documentView = originaltext
        clipView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .left, relatedBy: .equal, toItem: originaltext, attribute: .left, multiplier: 1.0, constant: 0))
        clipView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .top, relatedBy: .equal, toItem: originaltext, attribute: .top, multiplier: 1.0, constant: 0))
        clipView.addConstraint(NSLayoutConstraint(item: clipView, attribute: .right, relatedBy: .equal, toItem: originaltext, attribute: .right, multiplier: 1.0, constant: 0))
        
        abfrageNeuerText.accessoryView = scrollView
        let response = abfrageNeuerText.runModal()
        switch response {
        case .alertFirstButtonReturn:
            if originaltext.string.count == 0 {
                return
            }
            analyzeNewtext(forText: originaltext.string)
        default: return
        }
    }
1 Answers

You're complicating it for yourself with the auto layout. It's not necessary and the whole setup can be made much shorter:

let scrollView = NSScrollView(frame: NSRect(x:0, y:0, width: 200, height: 100))
scrollView.hasVerticalScroller = true

let clipView = NSClipView(frame: scrollView.bounds)
clipView.autoresizingMask = [.width, .height]

let textView = NSTextView(frame: clipView.bounds)
textView.autoresizingMask = [.width, .height]

clipView.documentView = textView
scrollView.contentView = clipView

Tested on Big Sur:

enter image description here

I can't test it with macOS Catalina right now (got it at another location), but I don't see a reason why it shouldn't work.

Side note - replace count == 0 with isEmpty.

Also check that I do assign textView to clipView.documentView & clipView to scrollView.contentView. It differs from your code.

If you're stuck you can always add Scrollable Text View to some window in the interface builder and inspect all the properties at runtime to learn how it was created and what values are set there.

Full code:

private func readNewText() {
    let alert = NSAlert()
    alert.alertStyle = .informational
    alert.messageText = "Input new text"
    alert.addButton(withTitle: "Convert")
    alert.addButton(withTitle: "Cancel")
    
    let scrollView = NSScrollView(frame: NSRect(x:0, y:0, width: 200, height: 100))
    scrollView.hasVerticalScroller = true
    
    let clipView = NSClipView(frame: scrollView.bounds)
    clipView.autoresizingMask = [.width, .height]
    
    let textView = NSTextView(frame: clipView.bounds)
    textView.autoresizingMask = [.width, .height]
    
    clipView.documentView = textView
    scrollView.contentView = clipView
    
    alert.accessoryView = scrollView
    
    let response = alert.runModal()
    switch response {
    case .alertFirstButtonReturn:
        if textView.string.isEmpty {
            return
        }
        print("analyze: \(textView.string)")
    default: return
    }
}
Related