I've spent quite a few days searching for a solution to putting an attributed string with attachment onto an NSPasteboard.
I can read an RTFD file with attachments, modify its text and attributes and then stick it on an NSPasteboard for use in other applications (Mail.app, for example) and that works fine. But what I'd like to do is also add an image at some point in the text. I can do that with text as attributed strings, but if I try inserting an image (as an attachment to an attributed string), the image never arrives (though the rest does).
It seems that RTFD comes in various flavors, and the one I think I need is serialized. I've tried many variations of NSPasteboard declared types, even using FileWrappers, but must be missing something important. No matter what I do, the attachment never seems to arrive.
The strange thing is, if I read an RTFD file that has image attachments, modify it and stick it in a pasteBoard, those original attachments work fine - if I try to add new attachments, they don't make it. An example is reading an RTFD file, working on it, loading pasteboard, and pasting the results into mail. All the original text and images, plus any new modified or added text and attributes show up, but an attached images is simply missing.
Here's some example code:
Make an attributed string with some text, then add an attached image, then a bit more text, display it in a textView (all that works), then load pasteboard and paste to textEdit or Mail... the attached image isn't there, though the rest is:
// get the image
NSImage *myImage = [[NSImage alloc] initWithData: [window dataWithPDFInsideRect:[theImage frame]]];
// set the image as an attachment
NSTextAttachment *myAttachment = [[NSTextAttachment alloc] init];
NSTextAttachmentCell *myAttachmentCell = [[NSTextAttachmentCell alloc] initImageCell:myImage];
[myAttachment setAttachmentCell:myAttachmentCell];
// put image inside attributed string
NSAttributedString *myImageString = [NSAttributedString attributedStringWithAttachment:myAttachment] ;
// make an attributes dictionary (simply makes text blue) as an example
NSDictionary *myAttributesDict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSColor blueColor], NSForegroundColorAttributeName,
nil];
// and add some beginning text
NSMutableAttributedString *theCombinedString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"Here's an image we just grabbed: \n\n"] attributes:myAttributesDict];
// now append our attached image
[theCombinedString appendAttributedString:myImageString];
// and add some following text as an example
NSMutableAttributedString *endString = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:#"\n\n How about that!\n"] attributes:myAttributesDict];
// and stick it all together
[theCombinedString appendAttributedString: endString];
// now display it in a textView to make sure we have something
[[junkTextView textStorage] appendAttributedString: theCombinedString];
/// --- works just fine to here --- ///
// the following loads the pastboard, including the added text, but for some reason, leaves out the above attachment
NSPasteboard *thePboard = [NSPasteboard generalPasteboard];
[thePboard clearContents];
NSAttributedString *theContents = [[NSAttributedString alloc] theCombinedString ];
[thePboard writeObjects:[NSArray arrayWithObject:theContents]];
// pasting into mail or textEdit shows the above before and after text, but not the image.
Any ideas?
I've tried using NSData, NSFileWrapper serialized, setting the various pasteboard types, and more. So far, nothing seems to work. If I load the image as TIFF data, it pastes fine, but I need it as an attributed string to insert into a larger string from a file that already has attachments.
This is my first posting here, so please excuse any formatting errors - I'll learn, and thanks so much for any pointers or help, even if it's RTFM, which I have done but may have mis-understood.
Finally found the solution, and it was a Wrapper after all. Here's the code for anyone interested, works with an image read from a file or grabbed from your app:
// make a file wrapper
NSFileWrapper* wrapper =[[NSFileWrapper alloc] initRegularFileWithContents:[theImage TIFFRepresentationUsingCompression:NSTIFFCompressionLZW factor:1]];
// -must- have this. used to save your pasted file in some apps
[wrapper setPreferredFilename:#"yourImage.tiff"];
//
NSAttributedString* imageString = [NSAttributedString attributedStringWithAttachment:[[NSTextAttachment alloc] initWithFileWrapper:wrapper]];
// then load pasteboard and paste wherever you wish. You can get fancier using private
// pasteboards and custom data types, of course. This is just a simple example.
Related
I am trying to make a UITextView to have two links in it as text, but with different colors. I am not sure if it's possible at all.
I have set the UITextView to detect links, not to be editable, but only selectable.
What I did so far is the following:
NSMutableAttributedString *termsAndConditionsTitle = [[NSMutableAttributedString alloc] initWithString:#"I have read the " attributes:#{NSLinkAttributeName: #"https://apple.com", NSForegroundColorAttributeName: [UIColor greenColor]}];
NSMutableAttributedString *someString2 = [[NSMutableAttributedString alloc] initWithString:#"terms and conditions" attributes:#{NSLinkAttributeName: #"https://microsoft.com", NSForegroundColorAttributeName: [UIColor redColor]}];
[termsAndConditionsTitle appendAttributedString:someString2];
[self.termsAndConditionsView setAttributedText:termsAndConditionsTitle];
But in the end the links just have the tint color of the UITextView. Is it possible to make the two link have different colors? Thanks in advance!
P.S. I don't want to use libraries/frameworks if possible.
Yes it is possible, the problem is that the TextView has some predefined attributes for the links.
To fix it you just have to remove them in this way:
yourTextView.linkTextAttributes = [:]
With this line of code if the textview detects a link it won't apply any special attribute to it. So it will work as usual but without changing the color of the links. If you want to change the colors, you'll have to do it manually when you are adding the attributes.
You'll need to assign your text view's linkTextAttributes - basically you just tell your text view to identify links and stylize them however you specify:
Objective-C
textView.linkTextAttributes = #{
NSForegroundColorAttributeName:[UIColor someColor],
NSUnderlineStyleAttributeName:#(NSUnderlineStyleSingle)
};
You should also be able to assign the tintColor (since UITextView's inherit the same behavior as UIView's and will use this color for links). But if that isn't working for whatever reason in your case, assigning the link attributes will be the solution.
If your intention was to have two different colors for each link, then you will have to manually assign the attributes of each link based on the range at which each link occurs in your string.
I have been struggling with this. User needs to enter text and/or emoticons in a TextView. I got an emoticon keyboard with my own images to enter emoticons. Problem is I need to keep a symbol (e.g. "(smile)" for the emoticon within the text while AT THE SAME TIME showing the emoticon picture on top of the symbol.
So user would see "Hello [the picture]" while the TextView.text property would return "Hello (smile)".
On Android you can use Spanned strings which allow you to cover part of your text with an image. Thus on Android I managed to achieve my objective without problem.
On iOS, I thought Attributed Strings were a similar concept to Spanned but so far all I have been able to do is entirely replace the emoticon's code with the picture (using NSTextAttachment). Is there a way to achieve my objective without having to maintain one attributed string containing pictures and one separate string containing codes?
You can use this method, Hope it will work for you.
- (NSAttributedString*) parseEmoticons:(NSAttributedString*)text {
text = [text stringByReplacingOccurrencesOfString:#":-)" withString:#"😄"];
text = [text stringByReplacingOccurrencesOfString:#";P" withString:#"😜"];
text = [text stringByReplacingOccurrencesOfString:#"B-)" withString:#"😎"];
text = [text stringByReplacingOccurrencesOfString:#";-P" withString:#"😜"];
return text;
}
Having failed to find a more elegant solution, I resorted to maintaining one attributedstring containing the emoticon picture, and one regular string to hold the emoticon codes. So my attString is for instance "Hello [Smiling picture]" while my string is "Hello %101%". If you are interested in building a chatting app as I am, here is the pseudo code:
In emoticon keyboard:
{
Insert picture into attributed string at location loc;
Call textView shouldChangeTextInRange:(loc,0) replacementText:"%101";
}
In the view controller at shouldChangeTextInRange:(loc,length) replacementText:text:
{
Parse regular string to jump over emoticon codes already there to find the location matching loc;
Replace text (for instance %101%) in regular string} at the identified location.
}
Note: shouldChangeTextInRange is also called for regular keyboard entries including delete.
My app allows users to format text in a UITextView by clicking some formatting buttons that apply attributes to the attributedText property of the text view. I want to allow users to copy their formatted text from one UITextView and paste it into another using the standard pasteboard and standard cut/copy/paste menu.
Currently if I copy formatted text from a UITextView and paste it into a new message in the Mail app, the formatting is preserved -- so the copying of formatted text is happening automatically. But if I paste the formatted text into another UITextView in my app, only the plain text appears.
By following the "Pasting the Selection" section in the Text Programming Guide for iOS, I was able to override the paste method of UITextView and intercept the pasted content:
- (void)paste:(id)sender {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
NSLog(#"types available: %#", [pasteboard pasteboardTypes]);
for (NSString *type in [pasteboard pasteboardTypes]) {
NSLog(#"type %# (%#): %#", type, NSStringFromClass([[pasteboard valueForPasteboardType:type] class]), [pasteboard valueForPasteboardType:type]);
}
}
This shows me that the pasteboard contains content in the following formats: com.apple.rtfd, public.rtf, public.text and "Apple Web Archive pasteboard type". The value for the text is a plain text string, the value for the rtf is an RTF string and the values for the two Apple formats are NSData.
This is where I'm stuck. How can I translate one of these items from the pasteboard data to an attributed string to set for the UITextView?
Or better yet, is there a way to configure the UITextView to accept formatted text automatically when pasting, in the same way that it supplies formatted text automatically when copying?
Duncan asked above, "Are you sure you have the uitextview set to handle rich text." I wasn't aware of a setting for that, but I checked the UITextView class reference again and found the allowsEditingTextAttributes property. I hadn't used that before because I was providing my own formatting buttons and didn't need to enable the system Bold/Italic/Underline options. But when I set that property to YES for my UITextView, then it started accepting formatted text automatically. Whew!
I'm using TTTAttributedLabel to detect clicks on links in a styled UILabel (using NSAttributedString), in an iOS 6 project. I'd like to be able to have alternating colors for links in my label; I'm fine with manually setting the different colors for different link ranges, as long as the library handles link-detection with user touches for me. It seems that the TTTAttributedLabel class applies link styling last, so that text styling for specific ranges is overwritten by the single link style set for the class instance.
Being about to dive in and try to modify the TTTAttributedLabel code (to either not apply link styling, or to apply my own style ranges afterwards), I figured I'd ask here whether anyone has better ideas to consider for achieving this. Might a different library support variously-colored link ranges in the same label, out of the box?
This is already supported, simply use:
- (void)addLinkWithTextCheckingResult:(NSTextCheckingResult *)result
attributes:(NSDictionary *)attributes;
This lets you specify your own attributes on a per-link basis. E.g., with a linkAttributes dictionary for one-off coloring of a link:
if (linkAttributes) {
[self addLinkWithTextCheckingResult:[NSTextCheckingResult linkCheckingResultWithRange:linkRange URL:[NSURL URLWithString:linkText]] attributes:linkAttributes];
}
else {
[self addLinkToURL:[NSURL URLWithString:linkText] withRange:linkRange];
}
The link attributes dictionary uses keys defined in NSAttributedString.h. For example:
linkAttributes = #{
NSForegroundColorAttributeName: [UIColor greenColor],
NSUnderlineStyleAttributeName: #(NSUnderlineStyleNone)
};
I am making an iPad app that uses UIWebView to display PDFs. I have a PDF that I would like to programmatically add links to. For simplicity, lets say there are 10 paragraphs. They are all numbered and have a few lines of text in them. I want to be able to somehow add a basic link to the PDF so that if paragraph 2 is touched, then my UIWebView can process the request that is associated with paragraph 2.
I have no idea what the structure of the PDF is like on the inside. I have no clue how to scale this to each paragraph of several hundred pages. But I am wondering if I can somehow add a link or HTML to the PDF so that I can manipulate it with my app.
Thanks!
To be clear, I am viewing this PDF on an iOS device but I recognize that the solution to my question might not have anything to do with Cocoa-touch frameworks. I am looking for any sort of solution that will allow me to add invisible links to certain areas of my PDF.
If you want an iPad app to recognize text fields, buttons, links from a pdf. You can edit the actual pdf (you'll need a version of Adobe Acrobat) and add those fields to the pdf. In your ios code parse the pdf fields using something like:
in a parse method:
-(void)parse:(CGPDFPageRef)page
{
for (int i = 0; i < CGPDFArrayGetCount (annotations); i++)
CGPDFArrayGetCount returns the number of items in a PDF array.
in the loop grab the field name:
if (CGPDFDictionaryGetString(dict, "T", &stringRef))
{
char *s = (char *) CGPDFStringGetBytePtr(stringRef);
fieldName = [NSString stringWithCString:s encoding:NSUTF8StringEncoding];
}
check to see what you want to do if a field name matches, say "button_1" or "hlink_3":
if ([fieldName isEqualToString:#"hlink_3"])
{
// do whatever, example add a button where the field was
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = rect;
[self addSubview:button];
}
There's a lot more to it, but this is the general idea.