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

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.

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.

Good Design for Contact Apps - Custom Table Cell Layout

Note: After reading, please edit title if not appropriate.
I'm just amateur in iOS. I practicing the objective-C with self-task and I'm trying to create a Contact Apps without any backend.
Problem:
In my app, I would like to display name and image in a cell. How can I achieve this?. I've already tried by adding to the sub view, the label(For Name) and image view (For Image) to a cell. But, it gives more conditional check for each of the different contact types. Like...
//In cellForRowAtIndexPath,
UILabel *lblForCell=[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 30)];
lblForCell.text=[arrForMyContacts objectAtIndex:indexPath.row];
[tblCellForContactTable.contentView addSubview:lblForCell];
if([lblForCell.text isEqual:#"Mom"]){
UIImageView *imgForContact=[[UIImageView alloc] initWithFrame:CGRectMake(200, 10, 60, 7)];
imgForContact.image=[UIImage imageNamed:#"Mom.png"];
[tblCellForContactTable.contentView addSubview: imgForContact];
}
//likewise condition increased for dad, bro etc...- bad design, Correct?
So tell me its there any other way to decrease condition.
There are several ways to do. First create an array of Dictionary. Something like this
NSMutableArray *contacts = [[NSMutableArray alloc] init];
NSDictionary *mom = [NSDictionary dictionaryWithObjectsAndKeys:#"MOM", #"Name",
#"mom.png", #"imageName",
nil];
NSDictionary *dad = [NSDictionary dictionaryWithObjectsAndKeys:#"DAD", #"Name",
#"dad.png", #"imageName",
nil];
NSDictionary *son = [NSDictionary dictionaryWithObjectsAndKeys:#"SON", #"Name",
#"son.png", #"imageName",
nil];
[contacts addObjectsFromArray:#[mom, dad, son]];
Second create a custom UIView class where you can render this dictionary with init method something like this
-(id)initWithContactInfo:(NSDictionary *)contactInfo;
Third at the tableView
//In cellForRowAtIndexPath,
NSDictionary *contact = [contacts objectAtIndex:indexpath.row];
MYCustomView *contactCell = [[MYCustomView alloc] initWithContactInfo:contact];
[cell.contentView addSubView:contactCell];
Making a custom View gives you flexibility of customizing your view for particular cell.
I hope it helps . Best of luck

iOS 7 using an HTML string as an NSAttributedString AND setting the font?

I have a UITextView in which I'm trying to display some attributed text which comes in the form of HTML. It uses things like inline bolding and italicizing. The client now wants the font on this to change. However, whenever I change the font, it seems to disregard any of the HTML formatting. Is there any way to change the font while retaining the other formatting attributes, like bolding an italicizing?
The way I was able to get it to work was to prepend my string with <span style=\"font-family: Avenir; font-size: 12\">%#</span>
For an objective-c approach, I used NSMutableString and -setAttributes:
NSString *htmlString = #"..."
NSData *textData = [htmlString dataUsingEncoding:NSUnicodeStringEncoding];
NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc]
initWithData:textData
options:#{ NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType }
documentAttributes:nil error:nil];
[attrStr setAttributes:#{NSFontAttributeName : [UIFont fontWithName:#"avenir" size:12.0f]} range:NSMakeRange(0,attrStr.length)];

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.

NSMutableAttributedString crashing on changing the font?

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