Mac OS X: strikethrough the text in a label (NSTextField)
Asked Answered
T

3

5

Is it possible to strikethrough the text in a label (NSTextField)?

I have tried to use the Font Panel, but apparently these are ignored when I try to set them:

enter image description here

enter image description here

Tyrr answered 3/9, 2011 at 14:44 Comment(0)
P
6

You can do it like this, assuming _textField is set as an outlet in your xib:

- (void) awakeFromNib
{
  NSMutableAttributedString *as = [[_textField attributedStringValue] mutableCopy];
  [as addAttribute:NSStrikethroughStyleAttributeName value:(NSNumber *)kCFBooleanTrue range:NSMakeRange(0, [as length])];
  [_textField setAttributedStringValue:[as autorelease]];
}

Edit:

If you want to write a custom strikethrough NSTextFieldCell subclass instead, the only method that should be necessary to override is setStringValue:

- (void) setStringValue:(NSString *)aString
{
  NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithString:aString];
  [as addAttribute:NSStrikethroughStyleAttributeName value:(NSNumber *)kCFBooleanTrue range:NSMakeRange(0, [as length])];
  [self setAttributedStringValue:[as autorelease]];
}
Perfidious answered 3/9, 2011 at 15:59 Comment(6)
Thanks for your response. The problem is that in the awakeFromNib I don't know the length of the text that will be inserted in the text field. In fact, I use Cocoa Bindings and I don't ever directly control the content of the text field. There is no a way to strikethrough a label regardless of its content?Tyrr
I understand more clearly now. NSTextFieldCell only has font and color attributes. You could solve this problem with a custom NSTextFieldCell subclass (it shouldn't be too difficult to write), or you could bind to a custom glue object that would call setAttributedStringValue: on your text field whenever its setter was called via bindings. Or you could forego bindings entirely. None of the solutions are particularly elegant, but I think they would work.Perfidious
Thanks for the great idea of subclass NSTextFieldCell. I create a subclass called StrikethroughTextFieldCell and I reimplement the method drawInteriorWithFrame:inView:: - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { [super drawInteriorWithFrame:cellFrame inView:controlView]; NSMutableAttributedString *as = [self.attributedStringValue mutableCopy]; [as addAttribute:NSStrikethroughStyleAttributeName value:(NSNumber *)kCFBooleanTrue range:NSMakeRange(0, as.length)]; [self setAttributedStringValue:as]; }. Unfortunately, however, does not work as expected.Tyrr
You should only have to override setStringValue:. I've updated my answer to show this. Also, obviously you'll need to set the custom cell class in IB.Perfidious
Thanks again for your time. Unfortunately, it seems to me that the method setStringValue:is never called when I use Cocoa Bindings. This is why I tried, unsuccessfully, to override drawInteriorWithFrame:inView:.Tyrr
Just a short update: it seems the setObjectValue:is called, probably because the value of my NSTextFieldCell is bounded to a NSNumber. Anyway, when the setAttributedStringValue is called it enters in a recursive loop...Tyrr
C
1

For me it works great combining the approach by sbooth of creating a custom NSTextFieldCell and overriding drawInteriorWithFrame:inView: as posted below:

- (void) drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
    [self setAttributedStringFromStringValue];
    [super drawInteriorWithFrame:cellFrame inView:controlView];
}


- (void) setAttributedStringFromStringValue {  // add strikethrough
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:self.stringValue];
    [attributedString addAttribute:NSStrikethroughStyleAttributeName value:(NSNumber *)kCFBooleanTrue range:NSMakeRange(0, attributedString.length)];
    [self setAttributedStringValue:attributedString];
}
Cd answered 10/6, 2013 at 22:53 Comment(0)
L
0

I'd like to share an easier approach with more options and also point to a mistake in earlier answers.

Easy
To bypass the string length, use initWithString:attributes: to apply the attributes on the entire string.

Mistake
All the above examples set a boolean value (kCFBooleanTrue) for NSStrikethroughStyleAttributeName. This is incorrect, since the data type of NSStrikethroughStyleAttributeName is an integer of type NSUnderlineStyle.
The reason why the above examples with kCFBooleanTrue work, is that they cast this boolean to an NSNumber, resulting in value 1. Which coincidentally matches that of a single line strikethrough NSUnderlineStyleSingle.

Bonus 1 - style
Using NSUnderlineStyle properly allows even more flexibility, for example by using a thick line (NSUnderlineStyleThick) or a double line (NSUnderlineStyleDouble).

Bonus 2 - color
The strikethrough color can also be defined in the attributes: dictionary via key NSStrikethroughColorAttributeName.

All this combined results in the following example:

// Show text with a red single line strikethrough:
NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"strikethrough"
                                                                         attributes:@{
    NSStrikethroughStyleAttributeName : [NSNumber numberWithInteger:NSUnderlineStyleSingle],
    NSStrikethroughColorAttributeName : [NSColor systemRedColor]
}];

_textField.attributedStringValue = text;
Lynnet answered 30/5 at 10:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.