Cocoa: pasting formatted text in the current font? - objective-c

When I put formatted text into the pasteboard (NSRTFPboardType), it pastes with all formatting preserved. But what I'd really like is to discard the font face and size information, while preserving the weight, color, etc.
According to the docs, an NSAttributedString with no font information will default to Helvetica 12, so that seems like a dead end.
I can also generate the text on demand, so if I could find out the font in the current UI element, I could modify the text before it goes into the pasteboard. I was hoping the accessibility API could help with this, but none of the attributes I can find in the UIElementInspector seem to deal with formatting.
Any ideas?
Here's a test case. It pastes in Helvetica 12 even though the only attribute is a green color:
// Create the string with no attributes, and strip the font just in case.
NSMutableAttributedString *s = [[[NSMutableAttributedString alloc] initWithString:#"Hello green world!"] autorelease];
[s removeAttribute:NSFontAttributeName range:NSMakeRange(0, [s length])];
// Add a test attribute
[s addAttribute:NSForegroundColorAttributeName value:[NSColor greenColor] range:NSMakeRange(6, 5)];
// Generate RTF data
NSData *rtf = [s RTFFromRange:NSMakeRange(0, [s length]) documentAttributes:nil];
// Copy to pasteboard
NSPasteboard *pb = [NSPasteboard generalPasteboard];
[pb declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil];
[pb setData:rtf forType:NSRTFPboardType];
Here's something interesting. If I try and generate the plainest raw RTF data I can, with absolutely no font information, it still pastes in Helvetica 12!
char *rawrtf = "{\\rtf1\\ansi\\ansicpg1252\n"
"Hello world.}";
NSData *rtf = [NSData dataWithBytes:rawrtf length:strlen(rawrtf)];
So if this is possible at all, I think it's only possible by querying the currently running application about the current font.

There is no system-wide or even application-wide notion of the "current font". The closest you can get is the typing attributes of a currently active NSTextView; if none is active, then there is nothing like a current font.
With that said, you could promise the RTF data to the pasteboard, and when it requests it, send the currently active application a copy AppleEvent command, wait for a response, pull any rich text off the pasteboard, and grab its font attributes. If no rich text is available, stick with your current font and size. I have no idea how well this heuristic works in practice, but I can't think of any better approach.
If I try and generate the plainest raw RTF data I can, with absolutely no font information, it still pastes in Helvetica 12!
The font defaults to Helvetica-12 when no font information is supplied because all text being drawn has to be in some font at some size. Helvetica-12 must have seemed as readable and sufficiently inoffensive to be chosen as the default. (Sure beats Comic Sans-72!)

NSAttributedString is immutable. I think perhaps what you need is an NSMutableAttributedString. This guide has an example of how to change things. You should be able to create a new NSMutableAttributedString as a copy of the NSAttributedString, and then make your modifications to it before passing it on to the pasteboard.
EDIT:
I'm not quite sure of what you what to do. You are saying that you want to preserve some formatting, and remove other formatting, and it seems like you understand how to manipulate the NSMutableAttributedString attributes, so what's the problem? Create a new attributed string, interrogate the old one, and apply whatever attributes you want to the new one, if you don't like the default font, change it. I'm not understanding where the problem is in this.
EDIT2:
Using initWithAttributedString: should copy the font properties over, but if it doesn't you should be able to use enumerateAttributesInRange:options:usingBlock: to interrogate your original NSAttributedString
EDIT3:
NSAttributedString *attrStr;
NSRange limitRange;
NSRange effectiveRange;
id attributeValue;
limitRange = NSMakeRange(0, [attrStr length]);
while (limitRange.length > 0) {
attributeValue = [attrStr attribute:NSFontAttributeName
atIndex:limitRange.location longestEffectiveRange:&effectiveRange
inRange:limitRange];
// apply attributeValue to the other mutable attributed string range here
limitRange = NSMakeRange(NSMaxRange(effectiveRange),
NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
}

changing copied text so it'll be different from what the user had copied would go against the idea of copy and paste, no?

Related

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)];

Change text of a label

I have a array that reads a .txt file and when you click a button the label changes in one of the words of the .txt file, but the label doen't change.
This is the code:
if(sender == self.button) {
NSArray *words = [[NSArray alloc] initWithObjects:#"words.txt", nil];
[randomLabel setText:[words objectAtIndex:random() % [words count]]];
}
What should I do so the label changes when I press the button?
What file do I use?
A few things here:
Reading in file into an array
Well, for starters you're not reading in the contents of the .txt file.
NSArray *words = [[NSArray alloc] initWithObjects:#"words.txt", nil];
This creates a 1 element array, with that one element being #"words.txt". I don't know the format of your .txt file, so I can't say for sure how you have to load it in. See How do I format a text file to be read in using arrayWithContentsOfFile on how to potentially do this.
Setting button text
Also, you need to make sure randomLabel actually refers to the label contained within the button, otherwise the button text won't change. Typically for a button, you'd change the title using the method:
- (void)setTitle:(NSString *)title forState:(UIControlState)state
So in your instance, it'd be:
NSString* newTitle = [words objectAtIndex:random() % [words count]];
[self.button setTitle:newTitle forState:UIControlStateNormal];
Is the code actually being called?
Double check that sender == self.button evaluates to true (for readability and clarity, I'd use [sender isEqual:self.button]). Use the debugger to step through the code, to see if that particular piece of code is being called. See http://mobile.tutsplus.com/tutorials/iphone/xcode-debugging_iphone-sdk/ on how to achieve this.
You should try using
(id)initWithContentsOfFile:(NSString *)aPath

Is there a way to change the font size in MFMailCompose?

I want to send a text file in the body of my email, using MFMailComposeController. Is there a way to change the font size?
NSString *file = [[NSString alloc] initWithContentsOfFile:fileName];
Look at MFMailComposeViewController's -setMessageBody:isHTML: for starters. If you specify a message body that is HTML, then you can also include CSS markup specified in an included <style> section for whatever font styling you like, among other things.

UILabel text as html text

I am stack with a small issue.
I need to use a sentence which will have first two words bold and last two words italic.
like I am using an Objective C Developer.
How to do that. Is this possible in Objective C?
Regards,
For iOS7 you can use this:
NSString * htmlString = #"<html><body> Some html string </body></html>";
NSAttributedString * attrStr = [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUnicodeStringEncoding] options:#{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
UILabel * myLabel = [UILabel alloc] init];
myLabel.attributedText = attrStr;
Apple recommends that for small amounts of styled text, you should use a web view, and display the text marked up in HTML and formatted with CSS, etc.
Personally I've never taken that advice, as I would consider a UI full of web views to be a bit over kill.
There is the Core Text framework, if you want a little more control over your text and want to use attributed strings.
It isn't a one-liner though. Using core text requires quite a lot of code.
I have written a core text view that will display tappable URL links inline with text, but I have not given it arbitrary formatting support. If you're interested in showing links within text, then check it out: https://github.com/jasarien/CoreTextHyperlinkView
You may be interested in Oliver Drobnik's rich text view, which is based on Core Text too. As far as I know, you can feed it HTML and it'll produce a native view containing your formatted text. Very useful. It can be found here: https://github.com/Cocoanetics/DTCoreText
I've written a very small class called THMultiPartLabel to help me accomplish this sort of thing - you can find it on GitHub here. It's based heavily on Jason's answer to a similar question here. Using this class, you'd implement your example like so:
UIFont *normal = [UIFont systemFontOfSize:20];
UIFont *bold = [UIFont boldSystemFontOfSize:20];
UIFont *italic = [UIFont italicSystemFontOfSize:20];
NSArray *fonts = [NSArray arrayWithObjects: normal, bold, normal, italic, normal nil];
THMultiPartLabel *mpLabel = [[THMultiPartLabel alloc] initWithOffsetX:0 Y:0 defaultFonts:fonts];
[mpLabel updateText:#"I ", #"am using ", #"an ", #"Objective C ", "Developer", nil];
You should have a look at Core Text.
Here are some useful resources:
Befriending Core Text on Cocoanetics.com
Core Text Reference Collection - official Apple documentation
SimpleTextInput - Example app by Apple
CoreTextPageViewer - Example app by Apple
You can't do both with the standard UILabel implementation. But you can do one or the other.
myLabel.font = [UIFont fontWithName:#"TrebuchetMS-Bold" size:18];
Your best option is probably to replace the UILabel with a UIWebView and use HTML to do the formatting.
[EDIT]
If you have lots of them and think there is too much of a performance hit, you can (from iOS 3.2 onwards) also consider NSAttributedString, although that will be a lot more coding.
Since iOS 6 you can use NSAttributedString with an UILabel, a NSAttributedString takes a NSString as an argument in one of its contructors, after that you can specify which ranges of this string has a particular text style.
You can find a good example on how to do it on iOS 6 here: http://weblog.invasivecode.com/post/31931916393/introduction-to-nsattributedstring-for-ios-6
If you what something backwards compatible, I'm strongly recommend the OHAttributedLabel which can be found here: https://github.com/AliSoftware/OHAttributedLabel
Note: OHAttributedLabel also allows the usage of html markup to style your label text.

Extracting a URL from within a string

My app receives numerous text strings which may or may not contain a URL anywhere within the string. What would be the best method to extract a URL from within a string? Thank you.
If you are working on a Mac application, Mac OS X 10.6 offers a new API to let you detect URLs with the spell checker. You may do it this way.
NSString *s = #"http://example.com"
NSInteger wordCount = 0;
NSOrthography *orthography = nil;
NSArray *checkResults = [[NSSpellChecker sharedSpellChecker] checkString:s range:NSMakeRange(0, [s length]) types:NSTextCheckingTypeLink options:nil inSpellDocumentWithTag:0 orthography:&orthography wordCount:&wordCount];
for (NSTextCheckingResult *result in checkResults) {
NSRange range = result.range;
NSURL *URL = result.URL;
}
The BSD-licensed AutoHyperlinks framework provides classes to scan text for URLs and either return them to you or mark them up as links in an attributed string.
I don't think it builds out of the box for the iPhone, but you could always add preprocessor directives to cut out any AppKit-dependent code. The scan-and-return interface should just work, once you get it to build. (Make sure to run the tests.)
Mike Abdullah wrote a patch for iPhone support. You might try that.