Since your NSTextView
is nested inside an NSScrolView
most of the times, you can play with the scroll view's margins.
The solution below is for storyboard-based app, which has a minium requirement of OS X 10.10 (Yosemite):
class ViewController: NSViewController {
@IBOutlet weak var scrollView: NSScrollView!
@IBOutlet weak var textView: NSTextView!
override func viewDidLoad() {
super.viewDidLoad()
textView.layoutManager?.delegate = self
scrollView.automaticallyAdjustsContentInsets = false
}
}
extension ViewController: NSLayoutManagerDelegate {
func layoutManager(_ layoutManager: NSLayoutManager, didCompleteLayoutFor textContainer: NSTextContainer?, atEnd layoutFinishedFlag: Bool) {
guard let textContainer = textContainer else { return }
let textRect = layoutManager.usedRect(for: textContainer)
let bottomSpace: CGFloat = 20.0
let bottomInset: CGFloat = (textRect.height + bottomSpace) > scrollView.bounds.height ? bottomSpace : 0
scrollView.contentInsets.bottom = bottomInset
scrollView.scrollerInsets.bottom = -bottomInset
}
}
Edit: how do I scroll the text view if I click on the bottom space?
You can play around with NSParagraphStyle
to add some after the last paragraph. Or you can use the Responder Chain and make view controller change the cursor position to the end of the text view:
class ViewController: NSViewController {
// ...
override func mouseUp(with event: NSEvent) {
let bottomSpace = scrollView.contentInsets.bottom
let clickLocation = self.view.convert(event.locationInWindow, to: textView)
let bottomRect = CGRect(x: 0, y: textView.bounds.height, width: textView.bounds.width, height: bottomSpace)
if bottomRect.contains(clickLocation) {
textView.moveToEndOfDocument(self)
}
}
}
If you want multiple text views with the same behaviour, time to design your own class.