views:

526

answers:

4

I have a Cocoa app with an NSTextView control which holds its text in an NSAttributedString (actually I believe it's a NSMutableAttributedString). I can easily set and modify different text attributes (such as font, underline, etc.) on different character ranges inside that string.

However, I want to set a part of the text as hidden (similar to the effect of the CSS attribute display: none). When an external event occurs (say a button clicked), I want to unhide or hide that specific range of characters.

Is there anyway to do this with NSAttributedString?

+2  A: 

As far as I know there are no invisible type attributes for NSAttributedString, however you could create a subclass of NSAttributedString (or the Mutable version, but that might not be necessary) that overrides the drawInRect: to avoid drawing the portion of the text that has your attribute.

But this could be a little messy.

littleknown
+2  A: 

The supported attributes for NSAttributedString are listed in AppKit's NSAttributedString.h header. There's no key like 'hidden' or 'visible'. The attributes (styles) are not derived from html and cannot express all css features.

Nevertheless, to hide a range of characters you can set the foreground color to transparent:

NSMutableAttributedString* myString;

[myString addAttribute:NSForegroundColorAttributeName
                 value:[NSColor clearColor]
                 range:NSMakeRange(0, 10)];
Nikolai Ruhe
+2  A: 

Another possibility would be to use a custom attribute on the text you want to hide, and then write your own method in a category on NSAttributedString that creates a new attributed string that excludes the text marked as hidden.

- (NSAttributedString *)attributedStringWithoutHiddenText {
    NSMutableAttributedString *result = [[[NSMutableString alloc] init] autorelease];
    NSRange fullRange = NSMakeRange(0, [self length]);
    NSRange range = NSZeroRange;
    while (NSMaxRange(range) < [self length]) {
        NSDictionary *attributes = [self attributesAtIndex:range.location longestEffectiveRange:&range inRange:fullRange];
        if ([[attributes objectForKey:MyHiddenTextAttribute] boolValue])
            continue;

        NSAttributedString *substring = [[NSAttributedString alloc] initWithString:[[self string] substringWithRange:range] attributes:attributes];
        [result appendAttributedString:substring];
        [substring release];
    }
    return result;
}

Caveat: I totally just wrote this off the top of my head, and it's not guaranteed to compile, work, light your hard drive on fire, not kick your dog, etc.

This would generate a string that's appropriate for drawing, but you would still need the original string for accessing any of the hidden text. Depending on the size of your strings, this could be a big memory overhead.

Alex
Oops, that should be NSMutableAttributedString in the very first line. Like I said, this hasn't been tested :-)
Alex
One should mention the difference between your solution and mine: Your code really removes the characters with the 'MyHiddenTextAttribute', whereas my solution leaves blank spaces at the marked Range.
Nikolai Ruhe
Yes, and it's important to keep in mind that this code creates a string that is only appropriate for display. The original question asked for something like the CSS display:none attribute, and when you draw the strings produced by this method, you'll get a similar effect.
Alex
Indeed Alex's solution is visually better, but is more complex (have to maintain the two strings, etc.).
dubek
A: 

I realize this is a very old thread, but the other other option is to do custom glyph rendering. There was a session about advanced text handling techniques at WWDC 2010 that covered code folding. This uses a similar technique to what you'll need to do with this and that is to examine the text as its being lain out and render the null glyph for your hidden text rather than the actual string. The session is Session 114 - Advanced Cocoa Text Tips and Tricks from WWDC 2010 videos. If you're a Mac Developer Program member, you can download these through the developer portal.

jxpx777