0

Is there a simple way to append a string to an NSScrollView's NSTextView? I don't want attributes. I simply have error messages coming in as NSString's, and I wish to append each to the window. Nothing fancy. No formatting beyond CR, LF and perhaps TAB if I have too many beers and decide to get over the top fancy.

Every path I follow through the Class docs seems to lead down into a self referential blackhole... Like NSMutableAttributedStrings... which aren't really NSStrings, and don't even have a cString method.

I have been considering just keeping my own NSString and complete rewriting the contents of the scroll view after appending the errorstring the easy way. But that seems... inefficient... when the numbers of reports could get quite large.

Dale Amon
  • 219
  • 2
  • 5

1 Answers1

1

Every string needs some attributes to be drawn to the screen — a font/size/colour at the very least is mandatory.

It's not entirely obvious from the docs, but the "proper" way to manipulate an NSTextView is by manipulating the NSTextStorage directly. Also, NSTextStorage is a subclass of NSMutableAttributedString.

You can add characters to the string without dealing with attributes, it will simply copy the attributes from the string around where you add the text to:

[textView.textStorage replaceCharactersInRange:NSMakeRange(textView.textStorage.length, 0) withString:@"\nhello world"];

You will probably also want to scroll down:

[textView scrollRangeToVisible:NSMakeRange(textView.storage.length, 0)];

Performance will be good, even up to gigabytes of data. NSTextView is very efficient, especially when only small changes to the content are made.

Abhi Beckert
  • 32,787
  • 12
  • 83
  • 110
  • I get Controller.m:58:26: error: ‘struct objc_object’ has no member named ‘storage’ [serverStatusMessages->storage replaceCharactersInRange:NSMakeRange(serverStatusMessages->storage.length, 0) withString: m]; ^ Controller.m:58:93: error: ‘struct objc_object’ has no member named ‘storage’ [serverStatusMessages->storage replaceCharactersInRange:NSMakeRange(serverStatusMessages->storage.length, 0) withString: m]; ^ – Dale Amon Apr 04 '14 at 04:43
  • Where serverStatusMessages is my outlet for the NSTextView – Dale Amon Apr 04 '14 at 04:44
  • Why are you using the `->` operator? Use the `.` operator instead. The `->` operator is a very low level language feature that is pretty much never used in Obj-C programming. They both do approximately the same thing, but `.` is a high level version that has additional features and usually it's the only one that can be used reliably. – Abhi Beckert Apr 04 '14 at 04:59
  • Did that first. Controller.m:58:26: error: request for member ‘storage’ in something not a structure or union [serverStatusMessages.storage replaceCharactersInRange:NSMakeRange(serverStatusMessages.storage.length, 0) withString: m]; ^ Controller.m:58:92: error: request for member ‘storage’ in something not a structure or union [serverStatusMessages.storage replaceCharactersInRange:NSMakeRange(serverStatusMessages.storage.length, 0) withString: m]; ^ – Dale Amon Apr 04 '14 at 05:01
  • Also I screwed up and posted `storage` when it should have been `textStorage`. :-) Try that. – Abhi Beckert Apr 04 '14 at 05:01
  • "not a structure or union" is a complicated error message due to legacy language features... that are rarely used. Usually it means "you typed the property name wrong". – Abhi Beckert Apr 04 '14 at 05:02
  • This is the line I was using to write single lines that overwrote every time: [serverStatusMessages setString: (NSString *) m]; – Dale Amon Apr 04 '14 at 05:03
  • Still get the same errors. Perhaps I need to type cast serverStatusMessages since it is like all outlets type id? – Dale Amon Apr 04 '14 at 05:07
  • Yep you'll need to typecast it, or else just declare it as NSTextView. Or you can use `[serverStatusMessages textStorage]` instead of the `.` syntax (which requires the compiler knowing what class the target is). The `.` syntax is neater code and executes faster though. – Abhi Beckert Apr 04 '14 at 05:10
  • Outlets usually aren't of type `id`. They should be the type of the object they point to. – Abhi Beckert Apr 04 '14 at 05:10
  • This finally did it: [((NSTextView *)serverStatusMessages).textStorage replaceCharactersInRange: NSMakeRange(((NSTextView *) serverStatusMessages).textStorage.length, 0) – Dale Amon Apr 04 '14 at 05:14
  • I will probably switch to using the methods to get the textStorage to simplify the code. Thanks for the friendly ear... or fingers as the case may be. – Dale Amon Apr 04 '14 at 05:15
  • You can avoid all that mess by using `@property (weak) IBOutlet NSTextView *serverStatusMessages;`. That's the recommended syntax, dunno where you got `id` from (replace weak with assign if ARC is disabled). – Abhi Beckert Apr 04 '14 at 05:18
  • id is automatically used for outlets by the code auto generated from GORM. – Dale Amon Apr 04 '14 at 05:19
  • @DaleAmon Then that is a bug in GORM. It shouldn't be id. I recommend you manually change them to have the proper declaration, it will help the compiler do a better job of detecting code mistakes and let you use the `.` operator on properties, improving performance. – Abhi Beckert Apr 04 '14 at 10:38