Permalink
Browse files

add support for text attachments

  • Loading branch information...
1 parent 6b345f5 commit d8622ccb515ff1e8c418ae29ad5d0300824ae1b4 Michael Ash committed Aug 29, 2011
Showing with 112 additions and 0 deletions.
  1. +18 −0 EGOTextView/EGOTextView.h
  2. +94 −0 EGOTextView/EGOTextView.m
View
@@ -27,6 +27,10 @@
#import <UIKit/UITextChecker.h>
#include <objc/runtime.h>
+
+extern NSString * const EGOTextAttachmentAttributeName;
+extern NSString * const EGOTextAttachmentPlaceholderString;
+
@class EGOTextView;
@protocol EGOTextViewDelegate <NSObject, UIScrollViewDelegate>
@optional
@@ -45,6 +49,18 @@
@end
+@protocol EGOTextAttachmentCell <NSObject>
+@optional
+
+// the attachment must either implement -attachmentView or both
+// -attachmentSize and -attachmentDrawInRect:
+- (UIView *)attachmentView;
+
+- (CGSize) attachmentSize;
+- (void) attachmentDrawInRect: (CGRect)r;
+
+@end
+
@class EGOCaretView, EGOContentView, EGOTextWindow, EGOMagnifyView, EGOSelectionView;
@interface EGOTextView : UIScrollView <UITextInputTraits, UITextInput> {
@private
@@ -84,6 +100,8 @@
EGOCaretView *_caretView;
EGOSelectionView *_selectionView;
+ NSMutableArray *_attachmentViews;
+
}
@property(nonatomic) UIDataDetectorTypes dataDetectorTypes; // UIDataDetectorTypeLink supported
View
@@ -26,6 +26,9 @@
#import "EGOTextView.h"
#import <QuartzCore/QuartzCore.h>
+NSString * const EGOTextAttachmentAttributeName = @"com.enormego.EGOTextAttachmentAttribute";
+NSString * const EGOTextAttachmentPlaceholderString = @"\uFFFC";
+
typedef enum {
EGOWindowLoupe = 0,
EGOWindowMagnify,
@@ -36,6 +39,28 @@
EGOSelectionTypeRight,
} EGOSelectionType;
+// MARK: Text attachment helper functions
+static void AttachmentRunDelegateDealloc(void *refCon) {
+ [(id)refCon release];
+}
+
+static CGSize AttachmentRunDelegateGetSize(void *refCon) {
+ id <EGOTextAttachmentCell> cell = refCon;
+ if ([cell respondsToSelector: @selector(attachmentSize)]) {
+ return [cell attachmentSize];
+ } else {
+ return [[cell attachmentView] frame].size;
+ }
+}
+
+static CGFloat AttachmentRunDelegateGetDescent(void *refCon) {
+ return AttachmentRunDelegateGetSize(refCon).height;
+}
+
+static CGFloat AttachmentRunDelegateGetWidth(void *refCon) {
+ return AttachmentRunDelegateGetSize(refCon).width;
+}
+
// MARK: EGOContentView definition
@interface EGOContentView : UIView {
@@ -145,6 +170,7 @@ - (void)removeCorrectionAttributesForRange:(NSRange)range;
- (void)insertCorrectionAttributesForRange:(NSRange)range;
- (void)showCorrectionMenuForRange:(NSRange)range;
- (void)checkLinksForRange:(NSRange)range;
+- (void)scanAttachments;
- (void)showMenu;
- (CGRect)menuPresentationRect;
@@ -303,6 +329,22 @@ - (void)textChanged {
CFRelease(frameRef);
}
+ for (UIView *view in _attachmentViews) {
+ [view removeFromSuperview];
+ }
+ [_attributedString enumerateAttribute: EGOTextAttachmentAttributeName inRange: NSMakeRange(0, [_attributedString length]) options: 0 usingBlock: ^(id value, NSRange range, BOOL *stop) {
+
+ if ([value respondsToSelector: @selector(attachmentView)]) {
+ UIView *view = [value attachmentView];
+ [_attachmentViews addObject: view];
+
+ CGRect rect = [self firstRectForNSRange: range];
+ rect.size = [view frame].size;
+ [view setFrame: rect];
+ [self addSubview: view];
+ }
+ }];
+
[_textContentView setNeedsDisplay];
}
@@ -348,6 +390,7 @@ - (void)setAttributedString:(NSAttributedString*)string {
NSRange range = NSMakeRange(0, _attributedString.string.length);
if (!_editing && !_editable) {
[self checkLinksForRange:range];
+ [self scanAttachments];
}
[self textChanged];
@@ -550,6 +593,25 @@ - (void)drawContentInRect:(CGRect)rect {
CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex((CFArrayRef)lines, i);
CGContextSetTextPosition(ctx, frameRect.origin.x + origins[i].x, frameRect.origin.y + origins[i].y);
CTLineDraw(line, ctx);
+
+ CFArrayRef runs = CTLineGetGlyphRuns(line);
+ CFIndex runsCount = CFArrayGetCount(runs);
+ for (CFIndex runsIndex = 0; runsIndex < runsCount; runsIndex++) {
+ CTRunRef run = CFArrayGetValueAtIndex(runs, runsIndex);
+ CFDictionaryRef attributes = CTRunGetAttributes(run);
+ id <EGOTextAttachmentCell> attachmentCell = [(id)attributes objectForKey: EGOTextAttachmentAttributeName];
+ if (attachmentCell != nil && [attachmentCell respondsToSelector: @selector(attachmentSize)] && [attachmentCell respondsToSelector: @selector(attachmentDrawInRect:)]) {
+ CGPoint position;
+ CTRunGetPositions(run, CFRangeMake(0, 1), &position);
+
+ CGSize size = [attachmentCell attachmentSize];
+ CGRect rect = { { origins[i].x + position.x, origins[i].y + position.y }, size };
+
+ UIGraphicsPushContext(UIGraphicsGetCurrentContext());
+ [attachmentCell attachmentDrawInRect: rect];
+ UIGraphicsPopContext();
+ }
+ }
}
free(origins);
@@ -1478,6 +1540,38 @@ - (void)checkLinksForRange:(NSRange)range {
}
+- (void)scanAttachments {
+
+ __block NSMutableAttributedString *mutableAttributedString = nil;
+
+ [_attributedString enumerateAttribute: EGOTextAttachmentAttributeName inRange: NSMakeRange(0, [_attributedString length]) options: 0 usingBlock: ^(id value, NSRange range, BOOL *stop) {
+ // we only care when an attachment is set
+ if (value != nil) {
+ // create the mutable version of the string if it's not already there
+ if (mutableAttributedString == nil)
+ mutableAttributedString = [_attributedString mutableCopy];
+
+ CTRunDelegateCallbacks callbacks = {
+ .version = kCTRunDelegateVersion1,
+ .dealloc = AttachmentRunDelegateDealloc,
+ .getAscent = AttachmentRunDelegateGetDescent,
+ //.getDescent = AttachmentRunDelegateGetDescent,
+ .getWidth = AttachmentRunDelegateGetWidth
+ };
+
+ // the retain here is balanced by the release in the Dealloc function
+ CTRunDelegateRef runDelegate = CTRunDelegateCreate(&callbacks, [value retain]);
+ [mutableAttributedString addAttribute: (NSString *)kCTRunDelegateAttributeName value: (id)runDelegate range:range];
+ CFRelease(runDelegate);
+ }
+ }];
+
+ if (mutableAttributedString) {
+ [_attributedString release];
+ _attributedString = mutableAttributedString;
+ }
+}
+
- (BOOL)selectedLinkAtIndex:(NSInteger)index {
NSTextCheckingResult *_link = [self linkAtIndex:index];

0 comments on commit d8622cc

Please sign in to comment.