Skip to content

Commit

Permalink
add support for text attachments
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Ash authored and Michael Ash committed Aug 29, 2011
1 parent 6b345f5 commit d8622cc
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 0 deletions.
18 changes: 18 additions & 0 deletions EGOTextView/EGOTextView.h
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -84,6 +100,8 @@
EGOCaretView *_caretView;
EGOSelectionView *_selectionView;

NSMutableArray *_attachmentViews;

}

@property(nonatomic) UIDataDetectorTypes dataDetectorTypes; // UIDataDetectorTypeLink supported
Expand Down
94 changes: 94 additions & 0 deletions EGOTextView/EGOTextView.m
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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];

}
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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];
Expand Down

0 comments on commit d8622cc

Please sign in to comment.