TweetFollow Us on Twitter

Using NSParagraphStyle

Volume Number: 19 (2003)
Issue Number: 8
Column Tag: Cocoa

Using NSParagraphStyle

How to programmatically set tabs and more in an NSTextView

by Clark Jackson

The need to become acquainted with NSParagraphStyle occurred when I attempted to automate some reporting. At times "enterprise" computing stands in contrast to typical "consumer" computing because the latter is user-centric while the former is report-centric. For example, consumer computing facilitates lots of user control whereas enterprise computing just wants to-generate-reports-unattended-at-midnight-thank-you. Since much of the text system in Cocoa "just works" for the user, the details of how to let the computer take control in making a report are often left a mystery. Let's shed a little light on some of that mystery.

What we need is to know how to use just one piece of Cocoa's extensive and capable text system that will allow a program to format text inside an NSTextView. The key is using the NSParagraphStyle class.

Some preliminaries

Explaining Interface Builder and Project Builder is beyond the scope of this article. Suffice it to say, the project uses the Model-View-Controller paradigm employing MyModel, MyWindowController, and NSTextView classes. Here's what the view looks like:


Figure 1. The interface window containing an NSTextView.

The only thing noteworthy about this window is that it contains an NSTextView because that is where our formatted text will appear.

To see the results of our formatting it will be useful to use NSTextView's ruler. To see the ruler it is necessary to add the Format menu to our menu bar. Therefore, before leaving IB, we drag the Format menu onto our project's main menu bar and give Show Ruler a key equivalent:


Figure 2. For convenience, in Interface Builder we drag a Format menu onto the menu bar and give Show Ruler a key equivalent.

The view

For our purposes, the important thing to know about NSTextView is that the text displayed in an NSTextView is its textStorage and that textStorage holds attributed strings. The textStorage is an instance of NSTextStorage which inherits from NSAttributedString itself. Attributed strings are Cocoa Foundation objects that contain both ordinary NSString's and formatting objects that apply to those strings, for example, NSFont and NSParagraphStyle. You can bypass dealing with NSTextView's textStorage directly, that is, you can [myNSTextView setString:@"aString"], but @"aString" will adopt either the default paragraph style or a previous style already residing in the textStorage.

Therefore, in order for text to appear formatted in an NSTextView we have to: 1) create an NSAttributedString instance initialized with the string we want to format, and 2) give that NSAttributedString instance the objects that it will use to format the string-in this case an NSParagraphStyle object. This attributed string goes into our NSTextView's textStorage where it appears formatted to the user.

The Controller

The interface buttons are connected to the controller's actions in a typical fashion. Basically, each button initiates a controller action to display a different formatted paragraph in the NSTextView. The controller does this task by sending a string (NSString) to the model object. The model object will return attributed strings (NSAttributedString's attributed with NSParagraphStyle's) to the controller. Finally, the controller will pass the attributed string to the text storage of the NSTextView where it will appear formatted to the user.

Here is one of the three controller actions:

applyStyle1

This method sets a pattern for the other two belonging to the controller. An unformatted string is 
put in the NSTextView for comparison purposes only. The program pauses so the user can see it. Next, 
the string is sent to the model object which returns it as an attributed string. Finally, the 
attributed string goes into the NSTextView revealing the new format. Refer to the source to see the 
other two action methods, applyStyle2 and applyStyle3. 

- (IBAction)applyStyle1:(id)sender
{
   MyModel *theModel;
   NSAttributedString *anAttString;
   NSString *aString;
   // a string containing multiple paragraphs
   aString = @"On a merry-go-round in the night, ";
   aString = [aString stringByAppendingString:
      @"Coriolis was shaken with fright.\n"];
   aString = [aString stringByAppendingString:
      @"Despite how he walked, "];
   aString = [aString stringByAppendingString:
      @"'Twas like he was stalked,\n"];
   aString = [aString stringByAppendingString:
      @"By some fiend always pushing him right."];
// limerick by David Morin, Eric Zaslow, E'beth Haley, John Golden, and Nathan Salwen,
//   http://www.aps.org/apsnews/11855.html
   // a convenience method to restore the default paragraph style to the NSTextView
   [self resetTextView]; 
   // install string w/o changing paragraph style for comparison
   [myNSTextView setString:aString]; 
   [myNSTextView display]; // force redraw
   sleep(1); // pause
   
   // now convert aString to an NSAttributedString by applying an NSParagraphStyle
   //   using the model object
   theModel = [[MyModel alloc]init];
   anAttString = [theModel attributeString1:aString];
   [theModel release];
   
   // now that we have the attributed string, put it into the NSTextView.
   // In order to get multiple attributed strings into a textStorage use
//      [myNSTextView insertText:attributedString] instead
   [[myNSTextView textStorage]
      setAttributedString:anAttString];
   [myNSTextView setNeedsDisplay:YES];
   return;
}

The Model

MyModel has a method for each of the controller's actions. It is here that we make use of NSParagraphStyle. Each method takes an ordinary NSString and returns it as an NSAttributedString so it can end up formatted in the NSTextView. When running the program, click inside the NSTextView and make the ruler visible. Some of the paragraph style attributes that we set show up on the NSTextView's ruler:


Figure 3. Many of NSParagraphStyle attributes appear in the NSTextView ruler.

Following are the methods used to create NSParagraphStyle's and apply them to strings thus creating NSAttributedString's:

attributeString1

This method creates an NSMutableAttributedString, using an NSString and an NSMutableParagraphStyle.

-(NSMutableAttributedString *) attributeString1:
      (NSString *) aString
{
   NSMutableParagraphStyle *aMutableParagraphStyle;
   NSMutableAttributedString   *attString;
   /*
   The only way to instantiate an NSMutableParagraphStyle is to mutably copy an
   NSParagraphStyle. And since we don't have an existing NSParagraphStyle available
   to copy, we use the default one.
   
   The default values supplied by the default NSParagraphStyle are:
      Alignment   NSNaturalTextAlignment
      Tab stops   12 left-aligned tabs, spaced by 28.0 points
      Line break mode   NSLineBreakByWordWrapping
      All others   0.0
   */
   aMutableParagraphStyle =
      [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
   
   // Now adjust our NSMutableParagraphStyle formatting to be whatever we want.
   // The numeric values below are in points (72 points per inch)
   [aMutableParagraphStyle
      setAlignment:NSLeftTextAlignment];
   [aMutableParagraphStyle setLineSpacing:5.5];
   [aMutableParagraphStyle setParagraphSpacing:25.5];
   [aMutableParagraphStyle setHeadIndent:25.0];
   [aMutableParagraphStyle setTailIndent:-45.0];
   // setTailIndent: if negative, offset from right margin (right margin mark does
   //      NOT appear); if positive, offset from left margin (margin mark DOES appear)
   [aMutableParagraphStyle setFirstLineHeadIndent:65.0];
   [aMutableParagraphStyle
      setLineBreakMode:NSLineBreakByWordWrapping];
   /*
    possible allignments
      NSLeftTextAlignment
      NSRightTextAlignment
      NSCenterTextAlignment
      NSJustifiedTextAlignment
      NSNaturalTextAlignment
    possible line wraps
      NSLineBreakByWordWrapping
      NSLineBreakByCharWrapping
      NSLineBreakByClipping
    */
   
   // Instantiate the NSMutableAttributedString with the argument string
   attString = [[NSMutableAttributedString alloc]
      initWithString:aString];
   // Apply your paragraph style attribute over the entire string
   [attString addAttribute:NSParagraphStyleAttributeName
      value:aMutableParagraphStyle
      range:NSMakeRange(0,[aString length])];
   [aMutableParagraphStyle release]; // since it was copy'd
   [attString autorelease]; // since it was alloc'd
   return attString;
}

If your NSTextView already has attributed strings in its textStorage, you can get the NSParagraphStyle by:

aMutableParagraphStyle = [[myTextView typingAttributes]
   objectForKey:@"NSParagraphStyle"];

The NSParagraphStyle returned above comes from the attributes of the text from where the cursor is found inside the NSTextView. NSParagraphStyle's can span multiple paragraphs but there cannot be more than one per paragraph.

attributeString2

This method creates an array of NSTextTab's and applies them to the NSMutableParagraphStyle before 
it is used to create the NSMutableAttributedString.

-(NSMutableAttributedString *)
   attributeString2:(NSString *) aString
{
   float firstColumnInch = 1.75,
      otherColumnInch = 0.6, pntPerInch = 72.0;
   int i;
   NSTextTab *aTab;
   NSMutableArray *myArrayOfTabs;
   NSMutableParagraphStyle *aMutableParagraphStyle;
   NSMutableAttributedString   *attString;
   myArrayOfTabs = [NSMutableArray arrayWithCapacity:14];
   aTab = [[NSTextTab alloc]
      initWithType:NSLeftTabStopType
      location:firstColumnInch*pntPerInch];
   [myArrayOfTabs addObject:aTab];
   [aTab release]; // aTab was alloc'd and the array owns it now so release it
   for(i=1;i<14;i++)
   {
      aTab = [[NSTextTab alloc]
         initWithType:NSRightTabStopType
         location:(firstColumnInch*pntPerInch)
         + ((float)i * otherColumnInch * pntPerInch)];
      [myArrayOfTabs addObject:aTab];
[aTab release]; // aTab was alloc'd and the array owns it now so release it
   }
   /*
   possible tab stop types
      NSLeftTabStopType
      NSRightTabStopType
      NSCenterTabStopType
      NSDecimalTabStopType
    */
    
   aMutableParagraphStyle =
      [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
   [aMutableParagraphStyle setTabStops:myArrayOfTabs];
   attString = [[NSMutableAttributedString alloc]
      initWithString:aString];
   [attString addAttribute:NSParagraphStyleAttributeName
      value:aMutableParagraphStyle
      range:NSMakeRange(0,[aString length])];
   [aMutableParagraphStyle release];
   [attString autorelease];
   return attString;
}

Tabbed text appears as below without using NSParagraphStyle:


Figure 4. Default behavior of an NSTextView containing tabbed text. The tab spacing is arbitrary and the lines wrap even though there is space enough to hold the whole line.

Tabbed text appears as below after creating an NSMutableParagraphStyle and assigning it to an NSMutableAttributedString using the attributeString2 method:


Figure 5. By using an NSMutableParagraphStyle the tab stops have been precisely set and the lines do not wrap unexpectedly.

attributeString3

This method supplies a way to parse a string containing multiple paragraphs and apply different 
paragraph styles to each.

-(NSMutableArray *) attributeString3:(NSString *)aString 
{
   NSRange myRange,rangeOfLine,rangeOfLineContent;
   unsigned startIndex,lineEndIndex,contentsEndIndex;
   NSMutableParagraphStyle
      *aMutableParagraphStyle1,*aMutableParagraphStyle2;
   NSMutableAttributedString   *attString;
   int lineCtr;
   NSMutableArray * arrayOfNSMutableAttributedString;
   NSString *pulledOutParagraph;
   arrayOfNSMutableAttributedString
      = [NSMutableArray arrayWithCapacity:5];
   /*
    lines can be terminated in the following ways for use with
      getLineStart:end:contentsEnd:forRange:
    *    U+000D (\r or CR), Mac
    *    U+2028 (Unicode line separator)
    *    U+000A (\n or LF), Unix
    *    U+2029 (Unicode paragraph separator)
    *    \r\n, in that order (also known as CRLF), Windows
    */
   /*
   Using getLineStart:end:contentsEnd:forRange: 
   @"Big\r\nMac    --example string
   in this case
      1st contentsEndIndex    is set to 3,
         the first character past the line content,
         line content being defined as the characters not including
         line termination character(s)
      1st lineEndIndex    is set to 5
         the first character past the end of line character(s)
   */
   // make 2 different paragraph styles
   aMutableParagraphStyle1 =
      [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
   aMutableParagraphStyle2 =
      [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
   [aMutableParagraphStyle1
      setAlignment:NSLeftTextAlignment];
   [aMutableParagraphStyle2
      setAlignment:NSRightTextAlignment];
   
   // initialize
   myRange = NSMakeRange(0,0); // (location,length)
   lineEndIndex = 0;
   lineCtr = 0;
   /*
    The while() block below walks down the string pulling out lines (paragraphs)
   including their end of line (paragraph) characters, converts them to attributed strings,
   and adds them to an array.
   The range in the getLineStart:end:contentsEnd:forRange: method has to be picky
   about where it's location is but doesn't have to be picky about length (i.e. 0 will do)
   */
   while(lineEndIndex < [aString length])
   {
      [aString getLineStart: &startIndex
         end: &lineEndIndex
         contentsEnd: &contentsEndIndex
         forRange: myRange];
      // rangeOfLineContent excludes line termination character(s)
      rangeOfLineContent =
         NSMakeRange(startIndex,contentsEndIndex-startIndex); 
      // rangeOfLine includes line termination character(s)
      rangeOfLine =
         NSMakeRange(startIndex,lineEndIndex-startIndex); 
      // apply paragraph style to pulledOutParagraph
      pulledOutParagraph =
         [aString substringWithRange:rangeOfLine];
      attString = [[NSMutableAttributedString alloc]
         initWithString:pulledOutParagraph];
      if(lineCtr % 2 == 0) // alternate paragraph styles on odd/even lines
      {
         // If range.length below is zero then the paragraph style is not applied.
         //   For strings with length > 0, range.length should be > 0 and <= length of the
         //   string so NSMakeRange(0,1) would work as well below
         //   provided you don't have an empty string.
         [attString addAttribute:NSParagraphStyleAttributeName
            value: aMutableParagraphStyle1
            range: NSMakeRange(0,[pulledOutParagraph length])];
      }
      else
      {
         [attString addAttribute:NSParagraphStyleAttributeName
            value:aMutableParagraphStyle2
            range:NSMakeRange(0,[pulledOutParagraph length])];
      }
      [arrayOfNSMutableAttributedString addObject:attString];
      [attString release]; // the array now owns it
      lineCtr++;
      // recalculate the range before looping
      myRange = NSMakeRange(lineEndIndex,0); 
   }
   [aMutableParagraphStyle1 release];
   [aMutableParagraphStyle2 release];
   return arrayOfNSMutableAttributedString;
}

Conclusion

Cocoa's text system is extensive involving many classes not even mentioned here. But if you just need to get some formatted, tabbed text into an NSTextView for that special report, the methods explained here should do the trick.


Clark Jackson test drove the first Mac and recognized that it was about to change the world. Lately he has been busy wearing many hats automating energy accounting in the rapidly changing electrical power industry at Tacoma Power. He can be contacted at cjackson@cityoftacoma.org.

 
AAPL
$531.70
Apple Inc.
+0.53
MSFT
$39.99
Microsoft Corpora
+0.05
GOOG
$534.81
Google Inc.
+6.19

MacTech Search:
Community Search:

Software Updates via MacUpdate

Viber 4.1.0 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Apple iOS 7.1.1 - The latest version of...
The latest version of iOS can be downloaded through iTunes. Apple iOS 7 brings an all-new design and all-new features. Simplicity Simplicity is often equated with minimalism. Yet true simplicity is... Read more
1Password 4.3 - Powerful password manage...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Lens Blur 1.3.0 - True out-of-focus boke...
Let Lens Blur transform your existing photo into true SLR-quality out-of-focus bokeh effect! Everyone needs a gorgeous personalized background for a social profile, blog, Web/UI design, presentation... Read more
VMware Fusion 6.0.3 - Run Windows apps a...
VMware Fusion allows you to create a Virtual Machine on your Mac and run Windows (including Windows 8.1) and Windows software on your Mac. Run your favorite Windows applications alongside Mac... Read more
Tweetbot 1.5.1 - Popular iOS twitter cli...
Tweetbot is a full-featured OS X Twitter client with a lot of personality. Whether it's the meticulously-crafted interface, sounds and animation, or features like multiple timelines and column views... Read more
Mac DVDRipper Pro 4.1.7 - Copy, backup,...
Mac DVDRipper Pro is the DVD backup solution that lets you protect your DVDs from scratches, save your batteries by reading your movies from your hard disk, manage your collection with just a few... Read more
PDFpenPro 6.2 - Advanced PDF toolkit for...
PDFpenPro allows users to edit PDF's easily. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Create... Read more
PDFpen 6.2 - Edit and annotate PDFs with...
PDFpen allows users to easily edit PDF's. Add text, images and signatures. Fill out PDF forms. Merge or split PDF documents. Reorder and delete pages. Even correct text and edit graphics! Features... Read more
Monolingual 1.5.9 - Remove unwanted OS X...
Monolingual is a program for removing unnecesary language resources from OS X, in order to reclaim several hundred megabytes of disk space. It requires a 64-bit capable Intel-based Mac and at least... Read more

Latest Forum Discussions

See All

Zeebox is Now Beamly TV – Still Offers t...
Zeebox is Now Beamly TV – Still Offers the Same Social Networking TV Fun, Plus a New Look Posted by Rob Rich on April 22nd, 2014 [ permalink ] | Read more »
Bandai Namco Unveils Upcoming Slate of M...
At their recent Global Gamers’ Day event, Bandai Namco was largely focused on their console and PC offerings for the upcoming year. However mobile still had a small presence, with some upcoming titles revealed by the company – though few were in a... | Read more »
The Story Behind Grammar Girl’s Grammar...
Learning is so much more rewarding when it’s also fun. That’s part of the reason why recent educational app, Grammar Pop HD, garnered such a positive review from us last week. The brainchild of Mignon Fogarty, otherwise known as Grammar Girl, we... | Read more »
Pinnacle Studio for iPhone Review
Pinnacle Studio for iPhone Review By Jennifer Allen on April 22nd, 2014 Our Rating: :: SIMPLY POWERFULiPhone App - Designed for the iPhone, compatible with the iPad Video editing while on the move has never been so convenient.   | Read more »
Taste Savant Review
Taste Savant Review By Jennifer Allen on April 22nd, 2014 Our Rating: :: TASTY RECOMMENDATIONSiPhone App - Designed for the iPhone, compatible with the iPad In need of some new restaurant recommendations? This app will be ideal,... | Read more »
Where’s My Water? Featuring XYY Adds Mor...
Where’s My Water? Featuring XYY Adds More Than Just Sheep to the Liquid Physics Puzzler Posted by Rob Rich on April 22nd, 2014 [ permalink ] | Read more »
FarmVille 2: Country Escape Review
FarmVille 2: Country Escape Review By Jennifer Allen on April 22nd, 2014 Our Rating: :: STEADY FARMINGUniversal App - Designed for iPhone and iPad FarmVille is bigger and brighter than ever before, but don’t expect it to sway you... | Read more »
Doctor Who: Legacy receives big update,...
Doctor Who: Legacy receives big update, adds Perk System and more Posted by Tre Lawrence on April 22nd, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Strongarm Universal Mount Hardware Revie...
Made by: Viatek Price: $14.99 for 2 Hardware/iOS Integration Rating: 3.5 out of 5 stars Usability Rating: 4 out of 5 stars Reuse Value Rating: 3.75 out of 5 stars Build Quality Rating: 3.5 out of 5 stars Overall Rating: 3.69 out of 5 stars | Read more »
Boxer Review
Boxer Review By Campbell Bird on April 21st, 2014 Our Rating: :: KO YOUR INBOXUniversal App - Designed for iPhone and iPad Turn your inbox into a task list or tune it to your own needs with this highly customizable, user friendly... | Read more »

Price Scanner via MacPrices.net

Updated iPad Price Trackers
We’ve updated our iPad Price Tracker and our iPad mini Price Tracker with the latest information on prices and availability from Apple and other resellers. Using a mobile device? We’ve also updated... Read more
Everything You Wanted To Know And Probably Mo...
Macworld UK’s Lou Hattersley takes a look inside Apple’s A7 System On Chip (SoC) , noting that its processor module is much more powerful than other smartphone chipsets. He notes that the A7 was a... Read more
Mavericks Now Runs Half Of Internet Connected...
Computerworld’s Greg Keizer reports that half of all Macs that went online in March were running OS X Mavericks with Version 10.9 accounting for the largest adoption percentage of any individual OS X... Read more
WinZip Mac 3 Simplifies Cloud Sharing And Pri...
WinZip has announced WinZip Mac 3, a new file sharing app that makes it simple to manage, protect and share files across email and the cloud. With direct support for Dropbox and Google Drive,... Read more
2.5GHz Mac mini, Apple refurbished, available...
The Apple Store has Apple Certified Refurbished 2.5GHz Mac minis available for $509, $90 off MSRP. Apple’s one-year warranty is included, and shipping is free. Read more
13-inch MacBook Pro, Apple refurbished, avail...
The Apple Store continues to offer Apple Certified Refurbished 13″ 2.5GHz MacBook Pros (4GB RAM/500GB HD) for $999 which is $200 off MSRP. Apple’s one-year warranty is included, and shipping is free. Read more
15-inch MacBook Pros (refurbished) available...
The Apple Store has Apple Certified Refurbished October 2013 15″ Retina MacBook Pros available starting at $1699, with models up to $400 off MSRP. Apple’s one-year warranty is standard, and shipping... Read more
$100 off cellular iPad minis plus free $20 gi...
Best Buy is discounting cellular iPad minis with Retina Displays by $100 on their online store for a limited time. They’ll also throw in a free $20 Best Buy gift card. Choose free shipping or free... Read more
Updated Price Trackers
We’ve updated our Mac Price Trackers with the latest information on prices, bundles, and availability on systems from Apple’s authorized internet/catalog resellers: - 15″ MacBook Pros - 13″ MacBook... Read more
Apple refurbished MacBook Airs available for...
The Apple Store has Apple Certified Refurbished 2013 MacBook Airs available for up to $200 off the cost of new models. An Apple one-year warranty is included with each MacBook, and shipping is free... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Inc. Research Data Specialist - Appl...
…of Worldwide Market Research & Intelligence. The team is responsible for conducting Apple branded consumer market research. It is also responsible for analyzing data Read more
*Apple* Automotive Parts Department position...
Apple Automotive is one of the fastest growing dealer…and it shows. Consider making the switch to the Apple Automotive Group today! At Apple Automotive, we Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.