26

OK, what I need should have been very simple. However, I've looked everywhere and I'm not sure I've found something that works 100% (and it's something that has troubled me in the past too).

So, here we are :

  • I want to be able to append to an NSTextView
  • After appending, the NSTextView should scroll down (so that that latest appended contents are visible)

Rather straightforward, huh?

So... any ideas? (A code example that performs exactly this simple "trick" would be more than ideal...)

Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223

5 Answers5

30

After cross-referencing several answers and sources (with some tweaks), here's the answer that does work (given _myTextView is an NSTextView outlet) :

- (void)appendToMyTextView:(NSString*)text
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSAttributedString* attr = [[NSAttributedString alloc] initWithString:text];

        [[_myTextView textStorage] appendAttributedString:attr];
        [_myTextView scrollRangeToVisible:NSMakeRange([[_myTextView string] length], 0)];
    });
}
Dr.Kameleon
  • 22,532
  • 20
  • 115
  • 223
  • 1
    Don't use `_myTextView`. Use `self.myTextView` instead. The `_myTextView` method of accessing a property should only be used when you are executing the line of code millions of times in rapid succession. This is especially true when using GCD, since `self.myTextView` is thread safe (depending how it's declared). – Abhi Beckert Apr 04 '14 at 04:18
14

The appendAttributedString and scrollToEndOfDocument are available starting in OS X 10.0 and 10.6 respectively

extension NSTextView {
    func append(string: String) {
        self.textStorage?.appendAttributedString(NSAttributedString(string: string))
        self.scrollToEndOfDocument(nil)
    }
}
notedible
  • 983
  • 7
  • 9
neoneye
  • 50,398
  • 25
  • 166
  • 151
  • Should one worry about memory management here when the total length of the string managed by the textview gets crazy long (> 100,000 lines)? e.g., when adding lines from a log file that grows indefinitely... – wcochran Jun 22 '17 at 22:19
  • @wcochran you need a custom textview for this that only has a small range of the file in memory. – neoneye Jun 23 '17 at 07:27
4

Simply use this way :

for (NSInteger i=1; i<=100; i++) {
    [self.textView setString:[NSString stringWithFormat:@"%@\n%@",[self.textView string],@(i)]];
}

[self.textView scrollRangeToVisible:NSMakeRange([[self.textView string] length], 0)];
Anoop Vaidya
  • 46,283
  • 15
  • 111
  • 140
  • That's almost the same with my approach (with the exception being that performing tricks on an `NSTextView` from the main thread, from my experience, is actually safer if you want to avoid glitches and all sorts of unexpected behaviours...). Thanks a lot for your answer, anyway! ;-) – Dr.Kameleon Mar 02 '13 at 09:54
  • You used blocks, and appended the textView's text, i re-wrote it, yours is better than mine. – Anoop Vaidya Mar 02 '13 at 09:58
3

Here's a Swift version of Anoop Vaidya's answer

extension NSTextView {
    func append(string: String) {
        let oldString = self.string == nil ? "" : self.string!
        let newString = NSString(format: "%@%@", oldString, string)
        self.string = newString
    }
}
Kaydell
  • 484
  • 1
  • 4
  • 14
-2

Here's a Swiftier solution:

extension NSTextView {
    func appendString(string:String) {
        self.string! += string
        self.scrollRangeToVisible(NSRange(location:countElements(self.string!), length: 0))
    }
}
estradin
  • 33
  • 1
  • 6