Core Text calculate letter frame in iOS
You did an impressive amount of work in your question and were so close on your own. The problem you were having comes from this line of code where you position the bounding boxes for each frame:
_characterFrames[ic].origin = CGPointMake(startOffset, lineOrigin.y);
The problem with it is that you are overriding whatever offset the frame already had.
If you were to comment out that line you would see that all the frames were positioned more or less in the same place but you would also see that they are not positioned at the exact same place. Some are positioned more to the left or right and some more up or down. This means that the frames for the glyphs have a position of their own.
The solution to your problem is to take the current position of the frames into account when you move them into their correct place on the lines. You can either do it by adding to x and y separately:
_characterFrames[ic].origin.x += startOffset;
_characterFrames[ic].origin.y += lineOrigin.y;
or by offsetting the rectangle:
_characterFrames[ic] = CGRectOffset(_characterFrames[ic],
startOffset, lineOrigin.y);
Now the bounding boxes will have their correct positions:
and you should see that it works for some of the more extreme fonts out there
Calculate Font Size to Fit Frame - Core Text - NSAttributedString - iOS
The only way I can see this being possible is to have a system that runs the size calculation then adjusts the size and repeats until it finds the right size.
I.e. set up a bisecting algorithm that goes between certain sizes.
i.e. run it for size 10.
Too small.
Size 20.
Too small.
Size 30.
Too big.
Size 25.
Too small.
Size 27.
Just right, use size 27.
You could even start in hundreds.
Size 100.
Too big.
Size 50.
etc...
Core Text - Height of glyph
Got there in the end, you can do it like this:
// Create an attributed string
CTLineRef line = CTLineCreateWithAttributedString(_string);
// Get an array of glyph runs from the line
CFArrayRef runArray = CTLineGetGlyphRuns(line);
// loop through each run in the array
CTRunRef run = ....
// Get the range of the run
CFRange range = CFRangeMake...
// Use CTRunGetImageBounds
CGRect glyphRect = CTRunGetImageBounds(run, context, range);
// glyphRect now contains the bounds of the glyph run, if the string is just 1 character you have the correct dimensions of that character.
How to calculate Frame size to draw CoreText by line
For some reasons seems that (ascent + descent)
isn't the correct height for a line. In fact ascender + 1 + descender is equal to the Point Size.
Simply changing this:
CGContextSetTextPosition(context, 0.0, idx*-(ascent + descent)-ascent);
into this:
CGContextSetTextPosition(context, 0.0, idx*-(ascent - 1 + descent)-ascent);
did the trick.
Reference: Cocoanetics article about UIFont.
Thanks to jverrijt for the hint!
How to calculate the width of a text string of a specific font and font-size?
You can do exactly that via the various sizeWithFont:
methods in NSString UIKit Additions. In your case the simplest variant should suffice (since you don't have multi-line labels):
NSString *someString = @"Hello World";
UIFont *yourFont = // [UIFont ...]
CGSize stringBoundingBox = [someString sizeWithFont:yourFont];
There are several variations of this method, eg. some consider line break modes or maximum sizes.
Related Topics
iOS Web Page Errors Over Cellular Data But Not Over Wifi? Recent Change to At&T Cellular Network
How to Make Generics in Collection Type Constraint
Swift - Fatal Error: Array Index Out of Range
Uicellview Cell Layout in Swift
iOS Swift Navigate to Certain Viewcontroller Programmatically from Push Notification
How to Convert an Pffile to an Uiimage with Swift
Decrypt Value from Blowfish in Objective-C Code
Swift UI Remove Extra List Empty Cells Without Navigation Controller
Detect Touch on Child Node of Object in Spritekit
What's the State of Developing iOS Apps in Linux
Uibutton Remove All Target-Actions
How to Clear or Clean Specific Pod from the Local Cocoapods Cache
Instagram Hooks Pre-Select Media Issue
Thread Error in Ibaction While Overriding Prepareforsegue Function