NSTextView non-editable areas of text?

2019-03-31 11:05发布

问题:

I have an NSTextView that contains data for the user to edit, but I want to surround it with a header and footer of non-editable data to give the user an idea of context.

I don't think an NSTextView can handle the concept of mixed editable/non-editable data, so I've come up with a few ideas.

a) Use text attachments with a custom cell to draw the header and footers.

b) Have 3 NSTextViews within the NSScrollView.

c) Use attributes to determine what cannot be edited,and use the delegate methods to prevent editing, this is probably my favourite, as it's probably the least intrusive.

Am I missing anything, any better ideas?

回答1:

The NSTextView delegate method -textView:shouldChangeTextInRange:replacementString: will let you do this. You can "just say NO" to change. ;-)

Update / Elaboration (November, 2015)

To elaborate based on the comments on this answer, the idea is to use your own custom attributes on the attributed string your text view is editing. Beyond the standard attributes, you can specify your own attribute name (any NSString) and PLIST-compatible object as the value for that name.

For example, if you wanted to designate a range of text as "uneditable", you could add an attribute for that range with an attribute named (for example) @"TextIsEditableAttributeName" with an NSNumber with a BOOL value of YES or NO: [NSNumber NO] or @( NO ) (to use ObjC number boxing - same result: an NSNumber instance). Later, when the text view asks its delegate if it should change text in range, you can inspect the range for the presence of your @"TextIsEditableAttributeName" attribute.

Really, there's only a need to assign an attribute to ranges that aren't editable, so you don't even have to check for the value. You could just put an empty NSData instance there for a placeholder so the attribute has a value. Your attribute name could be @"EditingLocked" or something. This means you only have to check for the presence of the @"EditingLocked" attribute anywhere in the proposed range and return NO when the text view asks. This would catch overlapped selection (if you allow selection for copying the non-editable text) of editable vs. non-editable ranges.

This same approach, of course, can work for -textView:willChangeSelectionFromCharacterRanges:toCharacterRanges:, another delegate method that allows you to return a "corrected" array of range values for selection. If you don't want to allow non-editable text to be selected, you can "cut out" the ranges described by any instances of your @"EditingLocked" attribute you find in the proposed ranges.

I hope this helps.