I am making some text viewer app. Currently I need very frequent and precise line handling ability, so I want to subclass NSTextStorage class. But I couldn't find any method to set a new text storage to NSTextView. The only method I could find was
-[NSLayoutManager replaceTextStorage:]
method. But it's confusing whether this is what I was looking for. Because it seems just replace text storage of linked NSLayoutManagers instead of NSTextView.
I also considered subclassing NSTextView and overriding -textStorage method, but if the class is not designed for subclassing, it will make undefined result.
Anyone has tried to use custom NSTextStorage on NSTextView? How can I do this? Or is this prohibited by design?
You can do something like this to change the storage for an NSTextView:
NSTextStorage *newStorage = [[NSTextStorage alloc] initWithString: #"test"];
[aTextView.layoutManager replaceTextStorage: newStorage];
Since NSTextStorage is a subclass of NSMutableAttributedString you can manipulate it with all of the same methods.
Related
I'm using the following code to make selected text bold in an NSTextView
[self.textView.attributedString addAttribute:NSFontAttributeName value:[NSFont boldSystemFontOfSize:12] range:self.textView.selectedRange];
Where self.textView is an outlet to an NSTextView . Xcode gives a warning that addAttribute may not work as the property is of type NSAttributedString and not NSMutableAttributedString.The code works but is it wrong to do it this way ? If so what is the proper way ?
UPDATE :
I found another way of doing this :
NSMutableAttributedString *textFieldText = [self.textView.attributedString mutableCopy];
[textFieldText addAttribute:NSFontAttributeName value:[NSFont boldSystemFontOfSize:12] range:self.textView.selectedRange];
[self.textView.textStorage setAttributedString:textFieldText];
Since both methods work , I'd like to know which is better.
General Remarks
Take the interface that a class exposes at its word. It is not best practice to assume that the return type is a specific subclass of the declared return type.
This is especially important in the context of a class cluster: different implementations of the common interface may have different different return types for the same method though those return types are guaranteed to be compatible with the type declared in the header.
Supposing you could be guaranteed that all undocumented subclasses in the class cluster presently return the same specific subclass of the type declared in the header, you cannot be guaranteed that that will remain the case in future revisions of Apple's frameworks.
NSTextView
The question is about an NSTextView's and its property textStorage. This property is of type NSTextStorage, a "semiconcrete subclass of NSMutableAttributedString". The documentation goes on, describing the preferred mechanism for changing the string stored by the NSTextStorage or its attributes:
use the text access methods defined by NSMutableAttributedString, NSAttributedString, NSMutableString, and NSString to perform character-level manipulation.
NSTextView exposes its property textStorage which is an instance of NSTextStorage. NSTextStorage is a subclass of NSMutableAttributedString. Consequently, we can simply add our attribute to it:
[self.textView.textStorage addAttribute:NSFontAttributeName
value:[NSFont boldSystemFontOfSize:12.0f]
range:self.textView.selectedRange];
I can access an app-wide delegate instance using [NSApp delegate] after adding an NSObject to the mainmenu.xib, setting the name of the object to the name of my appDelegate and setting the mainmenu.xib delegate to this object.
Now, what I would like to do, is to access to an object's Document, i.e. the active NSDocument that the object "belongs" to. It would be a doc-wide delegate instance I guess. Sometimes [self document] works, but not always. Is there a generic way?
There is no need to pass a reference explicitly. You may access the document from NSViewController in the following way:
id document = self.view.window.windowController.document;
What about [[NSDocumentController sharedDocumentController] currentDocument] ?
Be careful nevertheless.
Please read
NSDocumentController currentDocument returning nil
For any sub windows that are part of the document, it turns out that it's very easy to make a very simple subclass of NSViewController and store the required information in there. These view controllers are set up within the main Document implementation so it is easy to pass the address of the NSDocument object. Any actual subview can then be controlled by a view controller that is a subclass of this "managing controller".
This solution does not work for every object, but it does take the biggest hurdle and solves my problem...
I have a class MyImage, that have a NSAffineTransform member transform.
In my AppDelegate a have a NSMutableArray of images.
I have a MyView that draw the images from AppDelegate.images. So it must track changes in AppDelegate.images and redraw the content.
What is the best way to implement this though bindings?
You don't mean bindings, or if you do you have misunderstood how bindings work and what they are for.
For an explanation read this post - Manual binding in Cocoa
You need to use KVO to observe the array of images. You need to make sure you add and remove images from the array in a KVC compliant way.
It seems that NSTextView does not have the notification DidEndEditing and TextDidChange (which both exist for an NSTextField). Is there any similar functionality I can get out of the NSTextView? If not is there no way to know when the user has edited the text of the NsTextView?
Don't forget about superclasses. An NSTextView is a kind of NSText, and every NSText can have a delegate. It also, of course, posts matching notifications.
I tried to use the method setString to change the text in a textfield and it didn't work. Then I changed it to setStringValue and it worked. So what is setString used for?
NSTextField does not have a setString: method. NSTextField is a type of NSControl, and NSControl has a setStringValue: method.
NSText and its more famous subclass NSTextView have a setString: method. #John Boker is correct that the field editor is an NSText, but you still can't send setString: to an NSTextField, even in edit mode. You'd need to get the field editor from the window and then call setString: on that (not that you really should do that).
While it is confusing to newcomers, there is a good rationale behind the different methods. NSControl has a "value", and that value can take different types (setDoubleValue:, setObjectValue:, etc.) This allows me to set the value as a double, but then retrieve in as a string. For the broad range of possible controls, this makes good sense and is very flexible. NSText is not a control; note how it doesn't have setAction: or setTarget: either. Its purpose is to display and edit (attributed) strings. When you call string, you are actually getting the real backing text storage, not just the value of the object in string form (as stringValue does for NSControl).
That said, when you say "it didn't work," I hope you mean that you got a compiler warning about this that told you that NSTextField may not respond to setString:. If you're ignoring compiler warnings, then you're going to have a lot of problems in ObjC.
from: http://macosx.com/forums/software-programming-web-scripting/43905-_outlet-setstring-testing-selector-not-recognized.html#5
setString: is for NSText, which is the
class used for an NSTextField's field
editor while it is being edited, and
for the NSText subclass NSTextView
which can display and edit styled text
as in TextEdit.
So the way i take it is you can only use setString: when the field is in an edit mode, i think.
Going a bit more generically (in contrast to my comment on Rob's answer):
Objects respond to messages. You can send a message to any object that responds to that message; you can't send a message to any object that doesn't respond to it.
Instances of NSText and its subclass NSTextView respond to setString:, so you can send NSText objects and NSTextView objects setString: messages.
Instances of NSControl and its subclasses, including NSTextField, respond to setStringValue:, so you can send NSControl objects, NSTextField objects, and other control objects setStringValue: messages.
See Rob's answer and my comment on it for why these two specific branches of the class hierarchy have these seemingly-similar-but-differently-named methods.