UITextView - setting font size of attributedText slow? - iphone

I'm trying to change the font size of my UITextView's text, which is using attributed text, with the following:
UIFont *font = [self.textView.font fontWithSize:value];
NSDictionary *attributes = #{ NSFontAttributeName: font };
NSMutableAttributedString *attrString = [self.textView.attributedText mutableCopy];
[attrString addAttributes:attributes range:NSMakeRange(0, attrString.length)];
self.textView.attributedText = attrString;
However, using this in combination with a slider causes a noticeable slow down. Am I doing this correctly? Simple setting the font size of the textView.font property did not work.
This is how I'm setting the attributed text in the first place:
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:#"Neutra church-key brunch farm-to-table disrupt skateboard, bushwick next level organic gentrify street art. Pop-up echo park pork belly pour-over. Bespoke meggings put a bird on it, plaid hashtag farm-to-table YOLO freegan pug pickled jean shorts quinoa gentrify forage. Tumblr butcher echo park, small batch mumblecore banjo trust fund intelligentsia bushwick VHS raw denim."];
[string addAttribute:NSForegroundColorAttributeName value:[UIColor blueColor] range:NSMakeRange(59, 13)];
[string addAttribute:(NSString*)NSUnderlineStyleAttributeName value:[NSNumber numberWithInt:NSUnderlineStyleSingle] range:(NSRange){59, 13}];
textView.attributedText = string;

Related

How to set Line Spacing in UI Text View (Empty text view) to enter message with line space

In my app i need to set some line spacing on ui text view..
I know we can do it for non editable textviews / labels using paragraph style spacing
But in my app when i enter text it was not working,
I can do it only when i have a predefined text on it, if once i clear the text paragraph sty will not work
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
paragraphStyle.minimumLineHeight = 35.f;
paragraphStyle.maximumLineHeight = 35.f;
UIFont *font = [UIFont fontWithName:#"AmericanTypewriter" size:18.f];
NSString *string = #"This is a test";
NSDictionary *attributtes = #{
NSParagraphStyleAttributeName : paragraphStyle,
};
deedTextView.font = font;
deedTextView.attributedText = [[NSAttributedString alloc] initWithString:string
attributes:attributtes];
But, I dont have any pre defined text like NSString *string = #"This is a test";
Text view must be empty, while begin
I had the same Problem. Based on Sergius answer I came up with the following working solution.
The problem with Sergius answer was that all other already set attributes will be overwritten (Font, Color...)
So it is better to edit the existing typingAttributes:
NSDictionary* d = deedTextView.typingAttributes;
NSMutableDictionary* md = [NSMutableDictionary dictionaryWithDictionary:d];
[md setObject:paragraphStyle forKey:NSParagraphStyleAttributeName];
deedTextView.typingAttributes= md;
One simple option that comes to mind is the following.
Using one of those 2 methods from UITextViewDelegate you can achieve what you want:
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
- (void)textViewDidChange:(UITextView *)textView;
You algorithm is the following - take the textView.text, convert it to the style of your needs and set textView.text as textView.attributedText
You can also try setting this:
deedTextView.typingAttributes = [NSDictionary dictionaryWithObject:paragraphStyle forKey:NSParagraphStyleAttributeName];

How to get the range where UILabel will truncate a text [duplicate]

This question already has answers here:
Get truncated text from UILabel
(3 answers)
Closed 9 years ago.
I'm trying to figure out a way to understand at which range an instance of UILabel will truncate a text. I know how to get the size that a string would occupy using the -sizeWithFont:constrainedToSize:lineBreakMode:.
Let's say that we have a UILabel of about 5 lines and a long text, using the method above I'm able to know if it will fit or not. If it doesn't fit I'd like to add another UILabel with the remaining text. I'm doing that because the view layout is mixed with an image and when the image finish I'd like to have a text along the whole width of the view.
I know that with core text I can do that in just one view, but I'd prefer to go easy with UILabel.
/*IMAGE*/##/*TEXT*/
/*IMAGE*/##/*TEXT*/
/*IMAGE*/##/*TEXT*/
/*IMAGE*/##/*TEXT*/
/*IMAGE*/##/*TEXT*/
/*****TEXT*************/
/******TEXT*************/
/******TEXT**************/
Well I've found a solution the answer is a duplicate Get truncated text from UILabel
I copy the modified method from that answer, you need to import CoreText framework and be sure that the label is set to word wrap:
- (NSArray *)truncate:(NSString *)text forLabel: (UILabel*) label
{
NSMutableArray *textChunks = [[NSMutableArray alloc] init];
NSString *chunk = [[NSString alloc] init];
NSMutableAttributedString *attrString = nil;
UIFont *uiFont = label.font;
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
NSDictionary *attr = [NSDictionary dictionaryWithObject:(__bridge id)ctFont forKey:(id)kCTFontAttributeName];
attrString = [[NSMutableAttributedString alloc] initWithString:text attributes:attr];
CTFramesetterRef frameSetter;
CFRange fitRange;
while (attrString.length>0) {
frameSetter = CTFramesetterCreateWithAttributedString ((__bridge CFAttributedStringRef) attrString);
CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0,0), NULL, CGSizeMake(label.bounds.size.width, label.bounds.size.height), &fitRange);
CFRelease(frameSetter);
chunk = [[attrString attributedSubstringFromRange:NSMakeRange(0, fitRange.length)] string];
[textChunks addObject:chunk];
[attrString setAttributedString: [attrString attributedSubstringFromRange:NSMakeRange(fitRange.length, attrString.string.length-fitRange.length)]];
}
return textChunks;
}

How do I truncate a string within a string in a UILabel?

Say I have The Dark Knight Rises at 7:45pm and I need to fit that into a fixed-width UILabel (for iPhone). How would I make that truncate as "The Dark Knight Ris... at 7:45pm" rather than "The Dark Knight Rises at 7:4..."?
UILabel has this property:
#property(nonatomic) NSLineBreakMode lineBreakMode;
You enable that behaviour by setting it to NSLineBreakByTruncatingMiddle.
EDIT
I din't understand that you wanted to truncate only a part of the string.Then read this:
If you want to apply the line break mode to only a portion of the text, create a new attributed string with the desired style information and associate it with the label. If you are not using styled text, this property applies to the entire text string in the text property.
Example
So there is even a class for setting the paragraph style: NSParagraphStyle and it has also it's mutable version.
So let's say that you have a range where you want to apply that attribute:
NSRange range=NSMakeRange(i,j);
You have to create a NSMutableParagraphStyle object and set it's lineBreakMode to NSLineBreakByTruncatingMiddle.Notice that you may set also a lot of other parameters.So let's do that:
NSMutableParagraphStyle* style= [NSMutableParagraphStyle new];
style.lineBreakMode= NSLineBreakByTruncatingMiddle;
Then add that attribute for the attributedText of the label in that range.The attributedText property is a NSAttributedString, and not a NSMutableAttributedString, so you'll have to create a NSMutableAttributedString and assign it to that property:
NSMutableAttributedString* str=[[NSMutableAttributedString alloc]initWithString: self.label.text];
[str addAttribute: NSParagraphStyleAttributeName value: style range: range];
self.label.attributedText= str;
Notice that there are a lot of other properties for a NSAttributedString, check here.
You have to set the lineBreakMode. You can either do that from Interface Builder or programmatically as follows
label.lineBreakMode = NSLineBreakByTruncatingMiddle;
please note that since iOS 5 the type of such property changed from UILineBreakMode to NSLineBreakMode.
My first idea would be two labels side-by-side both with fixed width, but I'll assume you've ruled that out for some unstated reason. Alternatively, compute the truncation manually, like this ...
- (NSString *)truncatedStringFrom:(NSString *)string toFit:(UILabel *)label
atPixel:(CGFloat)pixel atPhrase:(NSString *)substring {
// truncate the part of string before substring until it fits pixel
// width in label
NSArray *components = [string componentsSeparatedByString:substring];
NSString *firstComponent = [components objectAtIndex:0];
CGSize size = [firstComponent sizeWithFont:label.font];
NSString *truncatedFirstComponent = firstComponent;
while (size.width > pixel) {
firstComponent = [firstComponent substringToIndex:[firstComponent length] - 1];
truncatedFirstComponent = [firstComponent stringByAppendingString:#"..."];
size = [truncatedFirstComponent sizeWithFont:label.font];
}
NSArray *newComponents = [NSArray arrayWithObjects:truncatedFirstComponent, [components lastObject], nil];
return [newComponents componentsJoinedByString:substring];
}
Call it like this:
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 160, 21)];
NSString *string = #"The Dark Knight Rises at 7:45pm";
NSString *substring = #"at";
CGFloat pix = 120.0;
NSString *result = [self truncatedStringFrom:string toFit:label atPixel:120.0 atPhrase:#"at"];
label.text = result;
This generates: #"The Dark Kni...at 7:45pm"

iOS: Bold and Italic on the same word

Background: I have been trying to display a sentence with Bold and Italic font as well as normal ones.
Question: How can I display something like this "Hello, my name is Byte". Notice that Byte is both bold and italic, while other words remains normal.
I have tried: I think coreText should be able to do something along the line, I just have not been able to find the correct way to do it. I also used TTTAttributeLabel and cannot make it both bold and italic. I have Three20 loaded, just do not know which or what to use. Webview does not work with my background.
As a reply to Carles Estevadeordal:
UIWebView *webView = [[UIWebView alloc]initWithFrame:CGRectMake(10, 360, 300, 40)];
[webView setBackgroundColor: [UIColor clearColor]];
[webView loadHTMLString:[NSString stringWithFormat:#"<html><body style=\"background-color: transparent;\">Hello, my name is <b><i>Byte</b></i></body></html>"] baseURL:nil];
[self.view addSubview:webView];
This is exactly the code, I used. It displayed white background.
After a good night sleep, I found a way to do it using TTTAtributedlabel.
Here is how:
TTTAttributedLabel *attLabel = [[TTTAttributedLabel alloc]initWithFrame:CGRectMake(x, y, xx, yy)];
NSString *text = #"Hello, my name is Byte";
[attLabel setText:text afterInheritingLabelAttributesAndConfiguringWithBlock:^(NSMutableAttributedString *mutableAttributedString) {
//font helvetica with bold and italic
UIFont *boldSystemFont = [UIFont fontWithName:#"Helvetica-BoldOblique" size:10];
CTFontRef font = CTFontCreateWithName((__bridge CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL);
NSRange boldRange = [[mutableAttributedString string] rangeOfString:#"Byte" options:NSCaseInsensitiveSearch];
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(__bridge id)font range:boldRange];
CFRelease(font);
return mutableAttributedString;
}];
I still do not have a way to add 2 attributes (ie: bold and italic separately) into the same word/letter. But this does the trick.
In case you are still interested in a CoreText solution to this issue :
newFont = CTFontCreateCopyWithSymbolicTraits(fontRef,
self.textSize,
NULL,
kCTFontBoldTrait | kCTFontItalicTrait,
kCTFontBoldTrait | kCTFontItalicTrait);
[attrString addAttribute:(NSString*)kCTFontAttributeName
value:(id)newFont
range:[copyString rangeOfString:customText.text]];
You can use this where sender is instance of UIButton
sender.titleLabel.font=[UIFont fontWithName:#"Helvetica-BoldOblique" size:18.0f];
To add two attributes (this is non ARC code):
#define kLabelFontSize 16.0
NSString labelString = #"Let's slide loudly";
[self.tttLabel setText:labelString afterInheritingLabelAttributesAndConfiguringWithBlock:^NSMutableAttributedString *(NSMutableAttributedString *mutableAttributedString) {
UIFont *boldSystemFont = [UIFont boldSystemFontOfSize:kLabelFontSize];
CTFontRef boldFont = CTFontCreateWithName((CFStringRef)boldSystemFont.fontName, boldSystemFont.pointSize, NULL);
if (boldFont) {
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(id)boldFont range:NSMakeRange(6, 11)];
CFRelease(boldFont);
}
UIFont *italicSystemFont = [UIFont italicSystemFontOfSize:kLabelFontSize];
CTFontRef italicFont = CTFontCreateWithName((CFStringRef)boldSystemFont.fontName, italicSystemFont.pointSize, NULL);
if (italicFont) {
[mutableAttributedString addAttribute:(NSString *)kCTFontAttributeName value:(id)italicFont range:NSMakeRange(12, 18)];
CFRelease(italicFont);
}
}];
return mutableAttributedString;
}];
If the sentence has a fixed format the easiest solution is to use 2 different UILabels, one with the normal font and the other one with a Bold and Italic font set.
If the sentence is dynamic then you should use a UIWebView object as it was a lavel and load a simple webpage with your text and the < b > < i > < /i > < /b > fields set at the spot when you want it bold and italic like:
[myWebViewLabel loadHTMLString:[NSString stringWithFormat:#"<html><body style=\"background-color: transparent;\">Hello, my name is <b><i>Byte</b></i></body></html>"] baseURL:nil];
I hope it helps.

Get truncated text from UILabel

Is there anyway I can get the truncated version of the text for a UILabel?
In short, I have a paragraph of text, and two UILabels - label A, which is 2 lines long, and label B, which is a variable height. Label A is above label B. The idea is that label A shows the first two lines of the paragraph of text, and upon a certain user action, label B because visible and displays the rest of the text.
I'm having trouble determining what should go in label B, as I don't know what's being shown in label A. I'd need to also remove the "..." from label A.
Note: I realize this is a bit convoluted but there are some good reasons for it, which I won't clutter up the question with.
I wonder if you could use the methods in the NSString UIKit Additions to figure out how much fits into label A.
A crude way might be to start with the first character of your text and test for the size it would take up (-sizeWithFont:forWidth:lineBreakMode: maybe?) and then keep adding characters one at a time until it doesn't fit into your label A any more.
I hope somebody else can come up with a better way to do this, but the above should work.
Update
Last night I looked a bit into Core Text for my own app and came across CTFramesetterSuggestFrameSizeWithConstraints. You could maybe use this to figure out how much of your string fits into the label, by looking at the fitRange in that function.
Update 2:
I think this should work, but I have just typed this in here, so it may not even compile:
UIFont *uiFont = [UIFont systemFontOfZise:13.0f]; // whichever font you're using
CTFontRef ctFont = CTFontCreateWithName((CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
NSDictionary *attr = [NSDictionary dictionaryWithObject:(id)ctFont forKey:(id)kCTFontAttributeName];
CFRelease(ctfont);
NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:yourLabelText attributes:attr];
CTFrameSetterRef frameSetter = CTFrameSetterCreateWithAttributedString((CFAttributedStringRef)attrString);
[attrString release];
CFRange fitRange;
CTFrameSetterSuggestFrameSizeWithConstrains(
frameSetter,
CFRangeMake(0, 0),
NULL,
CGSizeMake(labelWidth, labelHeight),
&fitRange);
CFRelease(frameSetter);
CFIndex numberOfCharactersThatFit = fitRange.length;
thanks to Thomas Müller
be sure to set line break mode the myLabel to this:
myLabel.lineBreakMode = NSLineBreakByWordWrapping;
by this method you can get chunked strings that actually fit in the constrained size.
Here is the baked code:
- (NSArray *)truncate:(NSString *)text
{
NSMutableArray *textChunks = [[NSMutableArray alloc] init];
NSString *chunk = [[NSString alloc] init];
CTFramesetterRef frameSetter;
UIFont *uiFont = [UIFont systemFontOfSize:17.0f];
CTFontRef ctFont = CTFontCreateWithName((__bridge CFStringRef)uiFont.fontName, uiFont.pointSize, NULL);
NSDictionary *attr = [NSDictionary dictionaryWithObject:(__bridge id)ctFont forKey:(id)kCTFontAttributeName];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:text attributes:attr];
CFRange fitRange;
while (attrString.length>0) {
frameSetter = CTFramesetterCreateWithAttributedString ((__bridge CFAttributedStringRef) attrString);
CTFramesetterSuggestFrameSizeWithConstraints(frameSetter, CFRangeMake(0,0), NULL, CGSizeMake(myLabel.frame.size.width, myLabel.frame.size.height), &fitRange);
CFRelease(frameSetter);
chunk = [[attrString attributedSubstringFromRange:NSMakeRange(0, fitRange.length)] string];
[textChunks addObject:chunk];
[attrString setAttributedString: [attrString attributedSubstringFromRange:NSMakeRange(fitRange.length, attrString.string.length-fitRange.length)]];
}
return textChunks;
}
For Label A, calculate approximate character that should fit perfectly for two lines, for the particular font you are using.
For label B, set variable Height that the whole text must fit into it.