Skip to content

Instantly share code, notes, and snippets.

@cypres
Created February 8, 2013 21:27
Show Gist options
  • Save cypres/4742069 to your computer and use it in GitHub Desktop.
Save cypres/4742069 to your computer and use it in GitHub Desktop.
//
// NSString+SizeCalculation.h
//
// Created by Hans Duedal on 24/01/13.
// Copyright (c) 2013 OnlineCity ApS.
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#import <Foundation/Foundation.h>
@interface NSString (SizeCalculation)
// Uses CoreText instead of UIStringDrawing, since UIStringDrawing is not thread safe
// More about the issue: http://stackoverflow.com/questions/12744558/uistringdrawing-methods-dont-seem-to-be-thread-safe-in-ios-6
- (CGSize) sizeWithFontName:(NSString*)fontName constraints:(CGSize)constraint targetSize:(CGFloat)target minimumSize:(CGFloat)minimum optimalFontSize:(CGFloat*)optimal fitRange:(NSRange*)range;
// This variant gives better results if you want to calculate the height,fontsize for a given width.
// Please note method assumes a single line of text.
- (CGFloat) heightWithFontName:(NSString*)fontName maxWidth:(CGFloat)maxWidth targetSize:(CGFloat)target minimumSize:(CGFloat)minimum optimalFontSize:(CGFloat*)optimal fitRange:(NSRange*)range;
@end
//
// NSString+SizeCalculation.m
//
// Created by Hans Duedal on 24/01/13.
// Copyright (c) 2013 OnlineCity ApS.
//
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby
// granted, provided that the above copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
#import "NSString+SizeCalculation.h"
#import <CoreText/CoreText.h>
@implementation NSString (SizeCalculation)
- (CGSize) sizeWithFontName:(NSString*)fontName constraints:(CGSize)constraint targetSize:(CGFloat)target minimumSize:(CGFloat)minimum optimalFontSize:(CGFloat*)optimal fitRange:(NSRange*)range
{
CFRange fitRange;
CGSize suggestion;
do {
id font = (__bridge_transfer id) CTFontCreateWithName((__bridge CFStringRef) fontName, target, NULL);
NSAttributedString *string = [[NSAttributedString alloc] initWithString:self attributes:@{(id)kCTFontAttributeName:font}];
id framesetter = (__bridge_transfer id) CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) string);
suggestion = CTFramesetterSuggestFrameSizeWithConstraints((__bridge CTFramesetterRef)(framesetter), CFRangeMake(0, self.length), nil, constraint, &fitRange);
} while (fitRange.length < self.length && target > minimum && (target -= 2.0f));
if (optimal != NULL) *optimal = target;
if (range != NULL) *range = NSMakeRange(fitRange.location,fitRange.length);
return suggestion;
}
// Using technique from https://github.com/jjgod/textus/blob/master/TTTextView.mm - use a CTTypesetter instead of a CTFramesetter
// The CTFramesetter gives suboptimal results for extreme cases, the height is larger than expected. CTTypesetter is faster as well.
- (CGFloat) heightWithFontName:(NSString*)fontName maxWidth:(CGFloat)maxWidth targetSize:(CGFloat)target minimumSize:(CGFloat)minimum optimalFontSize:(CGFloat*)optimal fitRange:(NSRange*)range
{
CFIndex count;
do {
id font = (__bridge_transfer id) CTFontCreateWithName((__bridge CFStringRef) fontName, target, NULL);
NSAttributedString *string = [[NSAttributedString alloc] initWithString:self attributes:@{(id)kCTFontAttributeName:font}];
id typesetter = (__bridge_transfer id) CTTypesetterCreateWithAttributedString((__bridge CFAttributedStringRef) string);
CFIndex start = 0;
count = CTTypesetterSuggestLineBreak((__bridge CTTypesetterRef) typesetter, start, maxWidth);
} while (count < self.length && target > minimum && (target -= 2.0f));
// Get the line height based the font
id font = (__bridge_transfer id) CTFontCreateWithName((__bridge CFStringRef) fontName, target, NULL);
CGFloat lineHeight = CTFontGetAscent((__bridge CTFontRef) font) + CTFontGetDescent((__bridge CTFontRef) font) + CTFontGetLeading((__bridge CTFontRef) font);
if (optimal != NULL) *optimal = target;
if (range != NULL) *range = NSMakeRange(0, count);
return lineHeight;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment