I\'m having real trouble getting my head around this issue.
As the title suggests, I have several UITextViews on a view in an iPhone application. I am programmatically c
Here's a peace of code i wrote to truncate a string (by words) to fit a certain width and height:
- (NSString *)stringByTruncatingString:(NSString *)string toHeight:(CGFloat)height maxWidth:(CGFloat)width withFont: (UIFont *)font {
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attrDict = [NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, nil];
NSMutableString *truncatedString = [string mutableCopy];
if ([string sizeWithAttributes: @{NSFontAttributeName: font}].height > height) {
// this line is optional, i wrote for better performance in case the string is too big
if([truncatedString length] > 150) truncatedString = [[truncatedString substringToIndex:150] mutableCopy];
// keep removing the last word until string is short enough
while ([truncatedString boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX) options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin attributes:attrDict context:nil].size.height > height) {
NSRange wordrange= [truncatedString rangeOfString: @" " options: NSBackwardsSearch];
truncatedString = [[truncatedString substringToIndex: wordrange.location] mutableCopy];
}
// add elipsis to the end
truncatedString = [NSMutableString stringWithFormat:@"%@...",truncatedString];
}
return truncatedString;
}
Usage:
NSString *smallString = [self stringByTruncatingString:veryBigString toHeight:65 maxWidth:200 withFont:[UIFont systemFontSize:14.0f]];
Because sizeWithFont:constrainedToSize:lineBreakMode:
is deprecated in iOS 7, I made a few changes:
- (NSString *)stringByDeletingWordsFromStringToFit:(CGRect)rect
withInset:(CGFloat)inset
usingFont:(UIFont *)font
{
NSString *result = [self copy];
CGSize maxSize = CGSizeMake(rect.size.width - (inset * 2), FLT_MAX);
if (!font) font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
CGRect boundingRect = [result boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font, } context:nil];
CGSize size = boundingRect.size;
NSRange range;
if (rect.size.height < size.height)
while (rect.size.height < size.height) {
range = [result rangeOfString:@" "
options:NSBackwardsSearch];
if (range.location != NSNotFound && range.location > 0 ) {
result = [result substringToIndex:range.location];
} else {
result = [result substringToIndex:result.length - 1];
}
if (!font) font = [UIFont systemFontOfSize:[UIFont systemFontSize]];
CGRect boundingRect = [result boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font, } context:nil];
size = boundingRect.size;
}
return result;
}
You can do a character count. For example, if you have UITextField like this:
+--------------------+
|This is a string in |
|a text view. |
+--------------------+
you have something like 20 characters per line. If you know that number you can simply truncate your string by -substringToIndex:
method.
int maxCharacters = 40; // change it to your max
if([myString length] > maxCharacters) {
myString = [myString substringToIndex:maxCharacters];
}
You can also think about UILabel
. Since you need only 2 lines of text, UILabel's numberOfLines
property can solve your problem.
Sadly, UILabel
with numberOfLines
wont do it if you need the view to be editable. Or you want UITextView
's (native) vertical alignment.
Here's an NSString
category that deletes words from a string, according to it's size in a rect:
@interface NSString (StringThatFits)
- (NSString *)stringByDeletingWordsFromStringToFit:(CGRect)rect
withInset:(CGFloat)inset
usingFont:(UIFont *)font
@end
@implementation NSString (StringThatFits)
- (NSString *)stringByDeletingWordsFromStringToFit:(CGRect)rect
withInset:(CGFloat)inset
usingFont:(UIFont *)font
{
NSString *result = [self copy];
CGSize maxSize = CGSizeMake(rect.size.width - (inset * 2), FLT_MAX);
CGSize size = [result sizeWithFont:font
constrainedToSize:maxSize
lineBreakMode:UILineBreakModeWordWrap];
NSRange range;
if (rect.size.height < size.height)
while (rect.size.height < size.height) {
range = [result rangeOfString:@" "
options:NSBackwardsSearch];
if (range.location != NSNotFound && range.location > 0 ) {
result = [result substringToIndex:range.location];
} else {
result = [result substringToIndex:result.length - 1];
}
size = [result sizeWithFont:font
constrainedToSize:maxSize
lineBreakMode:UILineBreakModeWordWrap];
}
return result;
}
@end
For a UITextView
, use an inset of 8 to account for the way UIKit draws them:
CGRect rect = aTextView.frame;
NSString *truncatedString = [theString stringByDeletingWordsFromStringToFit:rect
withInset:8.f
usingFont:theTextView.font];
Now UITextView
uses TextKit internally, it's much easier.
Rather than truncating the actual string, set the text
(or attributedText
) property to the whole string and truncate the amount of text displayed in the container (just like we do with UILabel
):
self.textView.scrollEnabled = NO;
self.textView.textContainer.maximumNumberOfLines = 0;
self.textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail;