NSMutableAttributedString crashing on changing the font? - objective-c

I'm sure mutable means it can be changed, so why's this happening?
attrString = [[NSMutableAttributedString alloc] initWithString:#"Tip 1: Aisle Management The most obvious step – although one that still has not been taken by a disconcerting number of organisations – is to configure cabinets in hot and cold aisles. If you haven’t got your racks into cold and hot aisle configurations, we can advise ways in which you can achieve improved airflow performance."];
[attrString setFont:[UIFont systemFontOfSize:20] range:NSMakeRange(0, 23)];
[attrString setFont:[UIFont systemFontOfSize:15] range:NSMakeRange(24, 325)];
[attrString setTextColor:[UIColor blackColor] range:NSMakeRange(0,184)];
[attrString setTextColor:[UIColor blueColor] range:NSMakeRange(185,325)];
break;
Both my catextlayer and my nsmutableattributedsring are defined in my header file. I make the changes to my string above in a switch, then call this code to update the catextlayer the string is shown in:
//updates catext layer
TextLayer = [CATextLayer layer];
TextLayer.bounds = CGRectMake(0.0f, 0.0f, 245.0f, 290.0f);
TextLayer.string = attrString;
TextLayer.position = CGPointMake(162.0, 250.0f);
TextLayer.wrapped = YES;
[self.view.layer addSublayer:TextLayer];
It crashes on when it tries to set the font, but I cant work out why?
-[NSConcreteMutableAttributedString setFont:range:]: unrecognized selector sent to instance 0xd384420
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSConcreteMutableAttributedString setFont:range:]: unrecognized selector sent to instance 0xd384420'
Why is this happening?

NSMutableAttributedString doesn't have a setFont:range: function.
Taken from here.... iphone/ipad: How exactly use NSAttributedString?
So I did a bit of reading from the docs.
The functions is...
[NSMutableAttirbutedString setAttributes:NSDictionary range:NSRange];
So you should be able to do something like this...
[string setAttributes:#{NSFontAttributeName:[UIFont fontWithName:#"Helvetice-Neue"]} range:NSMakeRange(0, 2)];
or
[string setAttributes:[NSDictionary dictionaryWithObjectsAndKeys:[UIFont fontWithName:#"Helvetice-Neue"], NSFontAttributeName", nil] range:NSMakeRange(0, 2)];
if you're still using old ObjC syntax.
Hope that helps.

First of all,Is the attrString you said a property?If it's a property,you'd better check that have you declared the property with the copy attribute, and are you presumably using the compiler-generated setter? If YES The compiler-generated setter sends the copy message to the object to make a copy. The copy message makes an immutable copy. That is, it creates an NSAttributedString, not an NSMutableAttributedString.
One way to fix this is to write your own setter that uses mutableCopy, like this if you're using ARC:
- (void)setTextCopy:(NSMutableAttributedString *)text {
textCopy = [text mutableCopy];
}
or like this if you're using manual reference counting:
- (void)setTextCopy:(NSMutableAttributedString *)text {
[textCopy release];
textCopy = [text mutableCopy];
}
Another fix would be to make textCopy be an NSAttributedString instead of an NSMutableAttributedString, and make the rest of your code work with it as an immutable object.
Reference:
1️⃣How to copy a NSMutableAttributedString
2️⃣NSConcreteAttributedString mutableString crash

Related

attributes not saving with file

This is probably easy, but I can not seam to figure it out - maybe it's late. I have a simple program that takes the text from an NSTextView and saves it as rtf. Saving the text itself works great, I just can not figure out how to get the attributes to tag along.
Code:
NSAttributedString *saveString = [[NSAttributedString alloc]
initWithString:[textView string]];
NSData *writeResults = [saveString
RTFFromRange:NSMakeRange:(0, [saveString length])
doumentAttributes:?? ];
[writeResults writeToURL:[panel URL] atomically: YES];
I know I need an NSDictionary for the documentAttributes, so how do I get that from the view?
What am I missing?
It seems that you are asking the textView for its string property. You need to ask it for its attributedString property:
NSAttributedString *saveString = textView.attributedString;
You can get the attributes from an attributed string like this:
NSMutableDictionary *allAttributes = [[NSMutableDictionary alloc] init];
[saveString enumerateAttribuesInRange:NSMakeRange(0,saveString.length) options:NSAttributedStringEnumerationReverse usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) {
[allAttrubutes addEntriesFromDictionary:attrs];
}];
NSData *writeResults = [saveString.string RTFFromRange:NSMakeRange(0,saveString.length) documentAttributes:allAttributes];
I have used this method to get attributes many times however I have never saved to RTF so I don't know exactly how this will turn out. All the attributes will be in the dictionary however.

NSTextStorage addAttribute for only specified range of characters

I have been working with TextKit and the NSTextStorage object on UITextView to attempt to get certain words to dynamically format.
The following method is in a subclass of UITextView and is executed on the textDidChange event. This works in that it does indeed detect when the word "the" is entered and it does color it red, however all text after the word "the" is also then red. The goal is for only "the" to be red.
Any idea what I am doing wrong?
- (void)highlight {
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"\\bthe\\b" options:0 error:nil];
NSArray *matches = [regex matchesInString:[self text] options:0 range:NSMakeRange(0, [self.text length])];
for (NSTextCheckingResult *match in matches) {
[self.textStorage beginEditing];
[self.textStorage addAttribute:NSForegroundColorAttributeName value:[UIColor redColor] range:match.range];
[self.textStorage endEditing];
}
}
This is happening because NSTextStorage is actually an NSMutableAttributedString subclass, and NSMutableAttributedString works in this way: it extends attributes to inserted text.
Try subclassing NSTextStorage (to so, you need to implement four methods, as mentioned in the NSTextStorage docs). Then, implement -fixAttributesInRange: in your NSTextStorage subclass. You'll need to manage text attributes in this method (such as highlighting the word "the", in the example that you gave) manually.
One trick to improve efficiency is to reprocess as little text as possible. One way to do so is to only process text for the current paragraph that has changed:
NSRange paragaphRange = [self.string paragraphRangeForRange:self.editedRange];
Where self.editedRange will be correctly populated by your superclass to be the range of the text that has just changed.

EXC_BAD_ACCESS on NSString sizeWithFont

I am not sure what is going on anymore. I have tried so many things to for me to figure out whats going on on this specific line of code that is causing a EXC_BAD_ACCESS.
I tried enabled NSZombies but it didn't help me any. Here is the code:
- (int)linesFromText:(NSString *)string withFont:(UIFont *)font andSize:(CGSize)size {
NSArray *splitString = [string componentsSeparatedByString:#" "];
NSMutableArray *allLines = [NSMutableArray array];
NSMutableString *line = [NSMutableString string];
NSString *word;
NSString *fakeLine;
for (int i = 0; i < splitString.count; i++) {
word = [splitString objectAtIndex:i];
fakeLine = [NSString stringWithFormat:#"%#%# ",line, word];
//NSLog(#"line %#, font %#",fakeLine,font);
////THIS IS THE LINE CAUSING THE EXC_BAD_ACCESS
CGSize lineSize = [fakeLine sizeWithFont:font];
if (lineSize.width <= size.width) {
[line appendFormat:#"%# ", word];
} else {
[allLines addObject:[line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
line = [NSMutableString string];
[line appendFormat:#"%# ", word];
}
}
[allLines addObject:[line stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
return allLines.count;
}
This is driving me crazy cause it only happens on the new iphone 5 when you scroll through the days to fast in the app I built. Here is a link to the application in the store:
http://itunes.apple.com/us/app/id543324451?mt=8
if you have an iphone 5 you can see what I mean. iphone 4 doesn't do this.
This code is being called in a UITableViewCell's layoutSubviews and is there to help size the frame of a Custom Attributed label that uses TTTAttributedLabel (https://github.com/mattt/TTTAttributedLabel).
I also tried to enable "Gaurd malloc to try give me more details but my XCode gives me this error:
dyld: could not load inserted library '/usr/lib/libgmalloc.dylib' because image not found
which if I look in /usr/lib that file is a sym link to a file that does exist in the same directory:
libgmalloc.dylib -> libgmalloc.B.dylib
I am running out of ideas here and thought it might be the UIFont getting released to soon and then it not being available so I put references in the UITableViewCell to hold the UIFont until the end of that Cell's life.
I have also searched around the internet and haven't found that much on this specifics.
Also here is an image of my stack trace from debugger:
http://i.stack.imgur.com/gWC5L.png
Any ideas?
Did I provide enough info?
Thanks
I think your question is answered here:
UIStringDrawing methods don't seem to be thread safe in iOS 6
The short version: sizeWithFont, and most other UIKit methods, are not thread safe when you're using it on the screen (rather than using it to pre-render).
The good news: look at Adam Swinden's answer in that thread; he explains how to get the same result in iOS6 with CoreText instead of UIKit.

NSInvalidArgumentException, nil argument

I've looked over the site for a while and I saw a few things but nothing worked for me. Extreme newbie here though.
I'm trying to make a 2 component picker, populated from arrays, populated from a dictionary, populated from a plist. The components populate, and I have a button that spins one component. The second button is meant to check answers (the first component has states which they try to match with capitals in the second component), but this is where it always crashes.
The error I get is as follows:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSPlaceholderString initWithFormat:locale:arguments:]: nil argument'
* First throw call stack:
(0x14b3022 0xeb3cd6 0x145ba48 0x145b9b9 0x916169 0x916ab4 0x3036 0x14b4e99 0x1914e 0x190e6 0xbfade 0xbffa7 0xbf266 0x3e3c0 0x3e5e6 0x24dc4 0x18634 0x139def5 0x1487195 0x13ebff2 0x13ea8da 0x13e9d84 0x13e9c9b 0x139c7d8 0x139c88a 0x16626 0x27ad 0x2715)
terminate called throwing an exception(lldb)
I'm not sure where I'm going wrong. I've done this with hard coded arrays, and it works fine. The code for the button is like so (please ignore the switch code, I haven't quite figured out how to make that work yet, I want the screen to turn green for correct answers and red for incorrect, but I'm more concerned with the app crashing!):
-(IBAction)checkAnswer;
{
NSInteger StateRow = [pickerCapital selectedRowInComponent:karrayStateComponent];
NSInteger CapitalRow =[pickerCapital selectedRowInComponent:karrayCapitalComponent];
NSString *state = [arrayState objectAtIndex:StateRow];
NSString *capital = [arrayCapital objectAtIndex:CapitalRow];
NSLog (#"%i",[pickerCapital selectedRowInComponent:karrayCapitalComponent]);
NSLog(#"%i", [pickerCapital selectedRowInComponent:karrayStateComponent]);
NSString *Title = [[NSString alloc]initWithFormat:#"You have selected %# as the capital of %#.", capital, state];
NSString *Message =[[NSString alloc]initWithFormat:lblMsg];
UIAlertView *alert =[[UIAlertView alloc]initWithTitle:Title message:Message delegate:#"" cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
if (StateRow == CapitalRow) {
lblMsg = #"You are correct!";
UIColor *myColor;
switch ([not sure what to use here]) {
case 0:
myColor = [UIColor greenColor];
break;
default:
break;
}
}
else {
lblMsg = #"Incorrect";
UIColor *myColor;
switch ([not sure what to use here]) {
case 0:
myColor = [UIColor redColor];
break;
default:
break;
}
}
}
If anyone can offer any sort of help on this, I would appreciate it greatly! Thank you!
Two things to check:
Where is lblMsg declared or assigned a value?
Are you sure capital and state are not nil from the calls to [arrayCapital objectAtIndex]?
Specifically: the NSString documentation states "Important: Raises an NSInvalidArgumentException if format is nil." for initWithFormat.

Why wont [NSTextStorage appendAttributedString: ] accept my NSAttributedString's attributes?

I am trying to put text with a specific font and color into an NSTextView. I can put the text in just fine, but it loses my attributes:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSFont fontWithName:#"Monaco" size:10], #"NSFontAttributeName",
[NSColor blueColor], #"NSForegroundColorAttributeName",
nil];
NSAttributedString *string = [[NSAttributedString alloc] initWithString:someNSString
attributes: dict];
[someNSTextStorage appendAttributedString:string];
I've read the all the relevant class references (NSTextView, NSTextStorage, NSAttributedString, etc...) and I still don't know why it doesn't work as I expect.
What am I doing wrong/missing?
Found your problem: You've enclosed NSForegroundColorAttributeName in quotes. So instead of a macro you're passing an NSString literal. Same goes for your NSFontAttributeName as well. Get rid of those and you're set.