iOS: How do you measure the width and height of a string using Quartz?
Asked Answered
A

2

19

Before I ask my questions, this is from Apple's documentation re: how to determine the width of a string using Quartz:

If text measurements are important to your application, it is possible to calculate them using Quartz 2D functions. However, you might first consider using ATSUI, whose strength is in text layout and measurement. ATSUI has several functions that obtain text metrics. Not only can you obtain after-layout text metrics, but in the rare cases you need them, you can obtain before-layout text metrics. Unlike Quartz, for which you must perform the calculations yourself, ATSUI computes the measurements for you. For example, you can obtain the image bounding rectangle for text by calling the ATSUI function ATSUMeasureTextImage.

If you decide that Quartz text suits your needs better than ATSUI (or Cocoa), you can follow these steps to measure the width of text before Quartz draws it:

  1. Call the function CGContextGetTextPosition to obtain the current text position.
  2. Set the text drawing mode to kCGTextInvisible using the function CGContextSetTextDrawingMode.
  3. Draw the text by calling the function CGContextShowText to draw the text at the current text position.
  4. Determine the final text position by calling the function CGContextGetTextPosition.
  5. Subtract the starting position from the ending position to determine the width of the text.

Here are my questions:

  1. Is this really the best way to determine the width of a string using Core Graphics? It seems flimsy and since my text co-exists with 2D graphical elements, I'd like to use the same context for all rendering. I was hoping there would be some compact method, like:

    CGContextGetTextWidthAndHeight(context, text);
    
  2. I read that ATSUI is outdated and going to be replaced by Core Text. Is that true and if so, is it in iOS?

Apperceive answered 27/5, 2009 at 0:46 Comment(1)
Core Text is now shipped in iOS 4. However, the methods pgb answered are most simple.Indopacific
C
22

On the iPhone SDK, there's a family of methods on NSString that provide what you want.

As of iOS 7.0, these methods are:

- boundingRectWithSize:options:attributes:context:
- sizeWithAttributes:

On older versions of iOS we had these, now deprecated:

– sizeWithFont:  
– sizeWithFont:forWidth:lineBreakMode:  
– sizeWithFont:constrainedToSize:  
– sizeWithFont:constrainedToSize:lineBreakMode:  
– sizeWithFont:minFontSize:actualFontSize:forWidth:lineBreakMode:
Cuttlebone answered 27/5, 2009 at 0:55 Comment(6)
I did not realize that you could mix and match Core Graphics calls with Objective-C objects... CG has its own text drawing function based on const char arrays but limited bells and whistles. It appears that "current context" simply means the last context instantiated, for example: CGContextSetFillColorWithColor(context, color.CGColor); [text drawAtPoint:origin withFont:font]; Thanks for the great advice!Apperceive
Actually, I'm fairly sure that by the current context they mean the context returned by UIGraphicsGetCurrentContext()Dowdell
You can not mix and match under specific circumstances - specifically; anything that calls UI___ (such as UIGraphicsGetCurrentContext()) from any thread other than the main thread will frequently fail. This is a serious issue, especially when you are using things like CATiledLayer - which performs it's rendering asynchronously on background threads.Hofuf
This will fail only under the following circumstances. When you have custom TextMatrix setup for the Current context, then you might have to multiply the size you get by that matrix again.Jehol
FYI: answer is from '09, this feature is depricated as of iOS 7, but still the #1 hit on Google.Stave
Updated with > iOS 7 methods.Cuttlebone
H
0

When performing drawing operations on threads other than the UI thread, you must use the technique you described. This is especially important to note when using things like CATiledLayer, which performs it's rendering asynchronously on background threads, or when drawing custom graphics in the background for any other reason.

I agree with PGB when you're doing "simple" graphics and you are guaranteed to be running on the main thread. However, sizeWithFont is doing the same thing as the technique you described - it's simply doing it for you using UIGraphicsGetCurrentContext().

Hofuf answered 19/10, 2010 at 8:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.