I need to restore the visual state of an NSAttributedString after having clicked on it.
My NSAttributedString contains links attributed to ranges.
In this example the text "@user" has a a link to "htpp://somesite.com/":
let text = "Hey @user!"
let attr = NSMutableAttributedString(string: text)
let range = NSRange(location: 4, length: 5)
attr.addAttribute(NSForegroundColorAttributeName, value: NSColor.orange, range: range)
attr.addAttribute(NSLinkAttributeName, value: "htpp://somesite.com/", range: range)
let tf = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 50))
tf.allowsEditingTextAttributes = true
tf.isSelectable = true
tf.stringValue = text
tf.attributedStringValue = attr
It works well: click on "@user" in the text field, it launches the URL.
But once clicked, the attributed color disappears and is replaced by this blue one and an underline is added:
I can't find a solution to restore the original color once the string is clicked (or to avoid having this automatic change altogether).
I've seen this and this but there's no actual solution, and I can't integrate the pointed library to my project (I'd really like to not having to import any library, actually).
Note that my existing code is in Swift but I can use an Objective-C solution.
When the link is clicked, the text is displayed by the field editor. The default link text style in the field editor is blue and underlined.
Solution 1: change the text style of the link in an override of setUpFieldEditorAttributes:
in a subclass of NSTextFieldCell
.
- (NSText *)setUpFieldEditorAttributes:(NSText *)textObj {
NSText *fieldEditor = [super setUpFieldEditorAttributes:textObj];
if ([fieldEditor isKindOfClass:[NSTextView class]]) {
NSMutableDictionary *linkAttributes = [((NSTextView *)fieldEditor).linkTextAttributes mutableCopy];
linkAttributes[NSForegroundColorAttributeName] = [NSColor orangeColor];
[linkAttributes removeObjectForKey:NSUnderlineStyleAttributeName];
((NSTextView *)fieldEditor).linkTextAttributes = linkAttributes;
}
return fieldEditor;
}
Side effect: the field editor is shared by all controls in the window and all controls will now show orange links.
Solution 2: substitute your own field editor by using the fieldEditor:forObject:
method or the windowWillReturnFieldEditor:toObject:
delegate method of NSWindow
. The text field has its own field editor and other controls won't have orange links. No subclasses of NSTextField
or NSTextFieldCell
required.
Example: (AppDelegate is the delegate of the window)
@interface AppDelegate ()
@property (weak) IBOutlet NSWindow *window;
@property (weak) IBOutlet NSTextField *textField;
@property (nonatomic, strong) NSTextView *linkFieldEditor;
@end
@implementation AppDelegate
- (NSTextView *)linkFieldEditor {
if (!_linkFieldEditor) {
_linkFieldEditor = [[NSTextView alloc] initWithFrame:NSZeroRect];
_linkFieldEditor.fieldEditor = YES;
NSMutableDictionary *linkAttributes = [_linkFieldEditor.linkTextAttributes mutableCopy];
linkAttributes[NSForegroundColorAttributeName] = [NSColor orangeColor];
[linkAttributes removeObjectForKey:NSUnderlineStyleAttributeName];
_linkFieldEditor.linkTextAttributes = linkAttributes;
}
return _linkFieldEditor;
}
- (id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client {
if (client == self.textField)
return self.linkFieldEditor;
else
return nil;
}
Solution 3: create a subclass of NSTextFieldCell
, implement fieldEditorForView:
and return your own field editor. This is similar to solution 2 but implemented by the cell instead of the window delegate.
Documentation on the field editor: Text Fields, Text Views, and the Field Editor and Using a Custom Field Editor.