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

Dev
  • 7,041
  • 6
  • 33
  • 63

2 Answers2

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]];
}
sbooth
  • 15,900
  • 2
  • 51
  • 76
  • 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? – Dev Sep 03 '11 at 17:38
  • 1
    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. – sbooth Sep 03 '11 at 22:54
  • 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. – Dev Sep 04 '11 at 10:30
  • 1
    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. – sbooth Sep 04 '11 at 12:15
  • 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:`. – Dev Sep 04 '11 at 12:23
  • 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... – Dev Sep 04 '11 at 12:34
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];
}
martn_st
  • 2,336
  • 1
  • 21
  • 27