1

I am currently writing many chunks of text on an NSView using the NSString helper method ... however it is very slow to write a large amount of in many cases repeated text. I am trying to re-write the code so that the text is converted to NSBezierPath's that are generated once, then drawn many times. The following will draw text at the bottom of the screen.

I am still trying to read through the apple documentation to understand how this works, but in the mean time I wonder if there is an easy way to alter this code to re-draw the path in multiple locations?

// Write a path to the view
NSBezierPath* path = [self bezierPathFromText: @"Hello world!" maxWidth: width];
[[NSColor grayColor] setFill];
[path fill];

Here is the method that writes some text into a path:

-(NSBezierPath*) bezierPathFromText: (NSString*) text maxWidth: (float) maxWidth {

// Create a container describing the shape of the text area,
// for testing done use the whole width of the NSView.
NSTextContainer* container = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(maxWidth - maxWidth/4, 60)];

// Create a storage object to hold an attributed version of the string to display
NSFont* font = [NSFont fontWithName:@"Helvetica" size: 26];
NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys: font, NSFontAttributeName, nil];
NSTextStorage* storage = [[NSTextStorage alloc] initWithString: text attributes: attr];

// Create a layout manager responsible for writing the text to the NSView
NSLayoutManager* layoutManger = [[NSLayoutManager alloc] init];
[layoutManger addTextContainer: container];
[layoutManger setTextStorage: storage];

NSRange glyphRange = [layoutManger glyphRangeForTextContainer: container];
NSGlyph glyphArray[glyphRange.length];
NSUInteger glyphCount = [layoutManger getGlyphs:glyphArray range:glyphRange];

NSBezierPath* path = [[NSBezierPath alloc] init];
//NSBezierPath *path = [NSBezierPath bezierPathWithRect:NSMakeRect(0, 0, 30, 30)];
[path moveToPoint: NSMakePoint(0, 7)]; 
[path appendBezierPathWithGlyphs:glyphArray count: glyphCount inFont:font];

// Deallocate unused objects
[layoutManger release];
[storage release];
[container release];

return [path autorelease];
}

Edit: I am attempting to optimise an application that outputs to screen, a sequence of large amounts text such as a sequence of 10,000 numbers. Each number has markings around it and/or differing amounts of space between them, and some numbers have dots and/or lines above, below or between them. Its like the example at the top of page two of this document but with much much more output.

Jay
  • 19,649
  • 38
  • 121
  • 184
  • Why do you have to draw so many times in the first place? – jtbandes Jul 03 '11 at 05:56
  • It appears `NSAffineTransform` can be used to permanently alter the co-ordinates, but what about if I want to re-draw multiple times in multiple different locations, its like I want a `[NSAffineTransform initPathWithTranslation: oldpath offset: offset]` – Jay Jul 03 '11 at 06:04
  • NB: The final answer was to rewrite all my code using the lower level core graphics and core text frameworks. Doing this means I can render views very fast on both desktop and iOS devices. – Jay Jul 06 '11 at 02:10

2 Answers2

1

You could start by removing the line:

[path moveToPoint: NSMakePoint(0, 7)];

so that your path isn't tied to a particular location in the view. With that done, you can call your method to get the path, move to a point, stroke the path, move to another point, stroke the path, and so on. If you want to move from the starting point within the path description, use -relativeMoveToPoint:.

Caleb
  • 124,013
  • 19
  • 183
  • 272
  • Sounds like what I am after, however I dont think I know enough to implement what you are suggesting. What do I put in the main function, just before the `[path fill]` call? ie `NSPath* path = ...; [path moveToPoint: NSMakePoint(0, 7)]; [[NSColor grayColor] setFill]; [path fill];` doesn't draw anything. – Jay Jul 03 '11 at 06:46
0

It appears this might do the trick, however I am not sure if it is the best way to do it?

// Create the path
NSBezierPath* path = [self bezierPathFromText: @"Fish are fun to watch in a fish tank, but not fun to eat, or something like that." maxWidth: width];

// Draw a copy of it at a transformed (moved) location
NSAffineTransform* transform = [[NSAffineTransform alloc] init];
[transform translateXBy: 10 yBy: 10];
NSBezierPath* path2 = [path copy];
[path2 transformUsingAffineTransform: transform];
[[NSColor greenColor] setFill];
[path2 fill];
[path2 release];
[transform release];
[path2 release];

// Draw another copy of it at a transformed (moved) location
transform = [[NSAffineTransform alloc] init];
[transform translateXBy: 10 yBy: 40];
path2 = [path copy];
[path2 transformUsingAffineTransform: transform];
[[NSColor greenColor] setFill];
[path2 fill];
[path2 release];
[transform release];
Jay
  • 19,649
  • 38
  • 121
  • 184