DetailViewController.h
@interface DetailViewController : UIViewController <UITextViewDelegate>
DetailViewController.m
- (BOOL)textView:(UITextView *)textView shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment inRange:(NSRange)characterRange { // save in ivar so we can access once action sheet option is selected _attachment = textAttachment; [self attachmentActionSheet:(UITextView *)textView range:characterRange]; return NO; } - (void)attachmentActionSheet:(UITextView *)textView range:(NSRange)range { // get the rect for the selected attachment (if its a big image with top not visible the action sheet // will be positioned above the top limit of the UITextView // Need to add code to adjust for this. CGRect attachmentRect = [self frameOfTextRange:range inTextView:textView]; FLOG(@" attachmentRect is %fx, %fy, %fw, %fh", attachmentRect.origin.x, attachmentRect.origin.y, attachmentRect.size.width, attachmentRect.size.height); _attachmentMenuSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy Image", @"Save to Camera Roll", @"Open in Viewer", nil]; // Show the sheet [_attachmentMenuSheet showFromRect:attachmentRect inView:textView animated:YES]; } - (CGRect)frameOfTextRange:(NSRange)range inTextView:(UITextView *)textView { CGRect rect = [textView.layoutManager boundingRectForGlyphRange:range inTextContainer:textView.textContainer]; // Now convert to textView coordinates CGRect rectRange = [textView convertRect:rect fromView:textView.textInputView]; // Now convert to contentView coordinates CGRect rectRangeSuper = [self.contentView convertRect:rectRange fromView:textView]; // Get the textView frame CGRect rectView = textView.frame; // Find the intersection of the two (in the same coordinate space) CGRect rectIntersect = CGRectIntersection(rectRangeSuper, rectView); // If no intersection then that's weird !! if (CGRectIsNull(rectIntersect)) { return rectRange; } // Now convert the intersection rect back to textView coordinates CGRect rectRangeInt = [textView convertRect:rectIntersect fromView:self.contentView]; return rectRangeInt; } - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex { if (actionSheet == _attachmentMenuSheet) { //FLOG(@"Button %d", buttonIndex); switch (buttonIndex) { case 0: //FLOG(@" Copy Image"); [self copyImageToPasteBoard:[_attachment image]]; break; case 1: //FLOG(@" Save to Camera Roll"); [self saveToCameraRoll:[_attachment image]]; break; case 2: //FLOG(@" Open in Viewer"); [self browseImage:[_attachment image]]; break; default: break; } } } - (void)saveToCameraRoll:(UIImage*)image { UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); } - (void)copyImageToPasteBoard:(UIImage*)image { UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; NSData *data = UIImagePNGRepresentation(image); [pasteboard setData:data forPasteboardType:@"public.png"]; } -(void)browseImage:(UIImage*)image { //LOG(@"browseImage: called"); OSImageViewController *_imageViewerController = [[OSImageViewController alloc] init]; UIImage *img = [[UIImage alloc] initWithData:UIImagePNGRepresentation(image)]; _imageViewerController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal; _imageViewerController.modalPresentationStyle = UIModalPresentationFullScreen; _imageViewerController.delegate = self; [self presentViewController:_imageViewerController animated:YES completion:^(void){ [_imageViewerController setImage:img]; }]; } - (void)dismissImageViewer { [self dismissViewControllerAnimated:YES completion:nil]; }