28 January 2009

Awesome Truncation for (nearly) Free!

Sometimes you need to shorten a string to make it fit within certain bounds. using the NSString - (void)drawAtPoint:(NSPoint)aPoint withAttributes:(NSDictionary *)attributes method will draw the string, but won't perform any truncation. The - (void)drawInRect:(NSRect)aRect withAttributes:(NSDictionary *)attributesmethod will truncate, but just clips at the end. Sometimes what we really want is the behavior that window titles show, an ellipsis at the end. Here's an example:

This behavior can be selected using the Inspector in Interface Builder for NSTextFields. But what do we do if we are drawing text in a custom view? It turns out that it is very simple to get automatic truncation, in at the head, in the middle, or at the tail of the string. Here's how:

  1. Add a paragraph style to the attributes dictionary. In this example, we do this in theinit method.
      // Set the paragraph style attribute to truncate the tail.
      NSMutableParagraphStyle* style = [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease];
      // Here we specify the truncation location with a constant.
      [style setLineBreakMode:NSLineBreakByTruncatingTail];
    
      // Make an attribute dictionary for the attributed string
      NSDictionary _textAttributes = [[NSDictionary dictionaryWithObjectsAndKeys:
            [NSFont systemFontOfSize:[NSFont labelFontSize]], NSFontAttributeName,
            style, NSParagraphStyleAttributeName,
            nil] retain];
    
  2. Calculate the rectangle in which you want to draw the string.
  3. Then draw the string using the NSString drawInRect:withAttributes:. Here's an example from our custom view's drawRect: method.
      [_text drawInRect:textRect withAttributes:_textAttributes];
    

There are actually three kinds of truncation that you can specify in this method, NSLineBreakByTruncatingHead NSLineBreakByTruncatingMiddle, and as observed, NSLineBreakByTruncatingTail. It is that easy.