Prevent Widows In UIKit Text Using NSString

21 July 2014

A widowed word is where a single word wraps onto a new line at the end of a paragraph. A lot of designers and typesetters do not like how this looks and will work to avoid it in documents. Although this is usually done by hand, for iOS development, the process needs to be automated. At first glance, UIKit views do not give this level of character-by-character control.1

However, through some understanding of Unicode and string manipulation, it is actually possible to prevent widowing using Foundation string processing alone. The key is using a non-breaking space. These characters are treated like dashes, so that two words are ‘connected’ and wrap together, but are drawn to the screen like normal whitespace. I use non-breaking spaces in my blog code to prevent titles from having widowed words. The same technique can be applied to UIKit labels and text views, as follows.

@implementation NSMutableString (RemoveWidows)

- (void)removeWidows {
   NSString *nonBreakingSpace = @"\u00a0"; // the unicode representation of a non-breaking space

   // enumerate substrings by paragraph
   [self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByParagraphs
      usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {

      // search for last normal space in paragraph
      NSRange range = [substring rangeOfString:@" " options:NSBackwardsSearch];

      if (range.length > 0) { // exists if range has a length
        // offset range by substringRange.location
   	NSRange adjustedRange = NSMakeRange(substringRange.location + range.location, range.length);

        // replace character with non-breaking space
   	[self replaceCharactersInRange:adjustedRange withString:nonBreakingSpace];
      }
   }];	
}

@end

A non-breaking space is expressed in a string literal as \u00a0. Using Foundation’s superb string processing algorithms, the script evaluates each paragraph of an input string separately, replacing the last occurrence of a normal space character with a non-breaking space.

This is implemented as a category on NSMutableString, converting characters in-place. However, it should be obvious that a ‘remove widows’ category for NSString is then easily derived.

@implementation NSString (RemoveWidows)

- (NSString *)stringByRemovingWidows {
   NSMutableString *outputString = self.mutableCopy; // make a mutable copy
   [outputString removeWidows]; // perform the transformation

   return [NSString stringWithString:outputString]; // return back as a new immutable string
}

@end

The method makes a mutable copy of itself (a string), calls the removeWidows method defined earlier and returns an immutable version, to match the interface definition.

The result can be seen in the screenshot at the top of the page. The first sentence has a widow. In the second sentence, however, the last words wrap cleanly as a group, so that a word cannot be left alone on a line. It’s a cool technique. Download the full source here.

1 You could use Text Kit, but doing so is unnecessarily time-consuming for basic UI work.