Created
March 29, 2012 21:18
-
-
Save wess/2243899 to your computer and use it in GitHub Desktop.
Content View using core text with 1 embedded image
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WCAttributedString.h | |
// iOS | |
// | |
// Created by Wess Cope on 3/19/12. | |
// | |
// Special Thanks to Erica Sadun's Cookbook For the following, updated just a bit. | |
// | |
#import <Foundation/Foundation.h> | |
@interface NSAttributedString (Height) | |
- (CGSize)sizeForAttributedStringWithWidth:(CGFloat)width; | |
@end | |
@interface WCAttributedString : NSObject | |
@property (nonatomic, strong) NSMutableAttributedString *string; | |
@property (nonatomic, strong) UIFont *font; | |
@property (nonatomic, strong) UIColor *color; | |
@property (nonatomic, strong) NSString *textAlignment; | |
@property (nonatomic, strong) NSString *linebreakMode; | |
+ (id)attributedString; | |
- (void) append:(NSString *)formattedString, ...; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WCAttributedString.m | |
// iOS | |
// | |
// Created by Wess Cope on 3/19/12. | |
// | |
// Special Thanks to Erica Sadun's Cookbook For the following, updated just a bit. | |
// | |
#import "WCAttributedString.h" | |
#import <CoreText/CoreText.h> | |
@implementation NSAttributedString (Height) | |
- (CGSize)sizeForAttributedStringWithWidth:(CGFloat)width | |
{ | |
CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedString((__bridge CFAttributedStringRef)self); | |
CFIndex offset = 0, length; | |
CGFloat y = 0; | |
do { | |
length = CTTypesetterSuggestLineBreak(typesetter, offset, width); | |
CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(offset, length)); | |
CGFloat ascent, descent, leading; | |
CTLineGetTypographicBounds(line, &ascent, &descent, &leading); | |
CFRelease(line); | |
offset += length; | |
y += ascent + descent + leading; | |
} while (offset < [self length]); | |
CFRelease(typesetter); | |
return CGSizeMake(width, ceil(y)); | |
} | |
@end | |
#define MATCHSTART(STR1, STR2) ([[STR1 uppercaseString] hasPrefix:[STR2 uppercaseString]]) | |
@implementation WCAttributedString | |
@synthesize string = _string; | |
@synthesize font = _font; | |
@synthesize color = _color; | |
@synthesize textAlignment = _textAlignment; | |
@synthesize linebreakMode = _linebreakMode; | |
- (id)init | |
{ | |
self = [super init]; | |
if (self) | |
{ | |
_string = [[NSMutableAttributedString alloc] init]; | |
_font = [UIFont systemFontOfSize:12.0f]; | |
} | |
return self; | |
} | |
+ (id)attributedString | |
{ | |
return [[self alloc] init]; | |
} | |
- (uint8_t) ctAlignment | |
{ | |
if (!_textAlignment) return kCTNaturalTextAlignment; | |
if (MATCHSTART(_textAlignment, @"n")) return kCTNaturalTextAlignment; | |
if (MATCHSTART(_textAlignment, @"l")) return kCTLeftTextAlignment; | |
if (MATCHSTART(_textAlignment, @"c")) return kCTCenterTextAlignment; | |
if (MATCHSTART(_textAlignment, @"r")) return kCTRightTextAlignment; | |
if (MATCHSTART(_textAlignment, @"j")) return kCTJustifiedTextAlignment; | |
return kCTNaturalTextAlignment; | |
} | |
- (uint8_t) ctBreakMode | |
{ | |
if (!_linebreakMode) return kCTLineBreakByWordWrapping; | |
if (MATCHSTART(_linebreakMode, @"word")) return kCTLineBreakByWordWrapping; | |
if (MATCHSTART(_linebreakMode, @"char")) return kCTLineBreakByCharWrapping; | |
if (MATCHSTART(_linebreakMode, @"clip")) return kCTLineBreakByClipping; | |
if (MATCHSTART(_linebreakMode, @"head")) return kCTLineBreakByTruncatingHead; | |
if (MATCHSTART(_linebreakMode, @"tail")) return kCTLineBreakByTruncatingTail; | |
if (MATCHSTART(_linebreakMode, @"mid")) return kCTLineBreakByTruncatingMiddle; | |
return kCTLineBreakByWordWrapping; | |
} | |
- (CTParagraphStyleRef) newParagraphStyle | |
{ | |
int addedTraits = 0; | |
if (_textAlignment) addedTraits++; | |
if (_linebreakMode) addedTraits++; | |
if (!addedTraits) return nil; | |
uint8_t theAlignment = [self ctAlignment]; | |
CTParagraphStyleSetting alignSetting = { | |
kCTParagraphStyleSpecifierAlignment, | |
sizeof(uint8_t), | |
&theAlignment}; | |
uint8_t theLineBreak = [self ctBreakMode]; | |
CTParagraphStyleSetting wordBreakSetting = { | |
kCTParagraphStyleSpecifierLineBreakMode, | |
sizeof(uint8_t), | |
&theLineBreak}; | |
CTParagraphStyleSetting settings[2] = {alignSetting, wordBreakSetting}; | |
CTParagraphStyleRef paraStyle = CTParagraphStyleCreate(settings, 2); | |
return paraStyle; | |
} | |
- (void) append:(NSString *) formatstring, ... | |
{ | |
if (!formatstring) return; | |
va_list arglist; | |
va_start(arglist, formatstring); | |
NSString *outstring = [[NSString alloc] initWithFormat:formatstring arguments:arglist]; | |
va_end(arglist); | |
CTFontRef basicFontRef = CTFontCreateWithName((__bridge CFStringRef)_font.fontName, _font.pointSize, NULL); | |
NSMutableDictionary *basicFontAttr = [NSMutableDictionary dictionaryWithObjectsAndKeys: | |
(__bridge id) basicFontRef, (__bridge NSString *) kCTFontAttributeName, | |
nil]; | |
CFRelease(basicFontRef); | |
if (_color) | |
[basicFontAttr setObject:(__bridge id) _color.CGColor forKey:(__bridge NSString *)kCTForegroundColorAttributeName]; | |
CTParagraphStyleRef style = [self newParagraphStyle]; | |
if (style) | |
{ | |
[basicFontAttr setObject:(__bridge id)style forKey:(__bridge NSString *)kCTParagraphStyleAttributeName]; | |
CFRelease(style); | |
} | |
NSAttributedString *newString = [[NSAttributedString alloc] initWithString:outstring attributes:basicFontAttr]; | |
[self.string appendAttributedString:newString]; | |
} | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WCContentView.h | |
// iOS | |
// | |
// Created by Wess Cope on 3/26/12. | |
// | |
#import <UIKit/UIKit.h> | |
@interface WCContentView : UIView | |
{ | |
NSAttributedString *_text; | |
UIImage *_image; | |
} | |
@property (nonatomic, readonly) CGFloat contentHeight; | |
- (id)initWithFrame:(CGRect)frame andText:(NSString *)text; | |
- (id)initWithFrame:(CGRect)frame text:(NSString *)text andImageURL:(NSString *)imageURL; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WCContentView.m | |
// iOS | |
// | |
// Created by Wess Cope on 3/26/12. | |
// | |
#import "BJContentView.h" | |
#import <CoreText/CoreText.h> | |
#import "SDImageCache.h" | |
#import "WCAttributedString.h" | |
@implementation BJContentView | |
@synthesize contentHeight = _contentHeight; | |
- (id)initWithFrame:(CGRect)frame andText:(NSString *)text | |
{ | |
self = [super initWithFrame:frame]; | |
if (self) | |
{ | |
self.backgroundColor = CLEAR; | |
self.opaque = NO; | |
WCAttributedString *aString = [WCAttributedString attributedString]; | |
aString.font = [UIFont systemFontOfSize:18.0f]; | |
[aString append:text]; | |
_text = aString.string; | |
CFRange range; | |
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFMutableAttributedStringRef)_text); | |
CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter, CFRangeMake(0, 0), nil, CGSizeMake(frame.size.width, 10000), &range); | |
_contentHeight = textSize.height + 150.0f; | |
frame.size.height = _contentHeight; | |
self.frame = frame; | |
_image = nil; | |
CFRelease(framesetter); | |
} | |
return self; | |
} | |
- (id)initWithFrame:(CGRect)frame text:(NSString *)text andImageURL:(NSString *)imageURL | |
{ | |
self = [self initWithFrame:frame andText:text]; | |
if(self) | |
{ | |
UIImage *cachedImage = [[SDImageCache sharedImageCache] imageFromKey:imageURL]; | |
if(!cachedImage) | |
{ | |
cachedImage = IMAGE_FROM_URL(imageURL); | |
BOOL isLandscapeImage = ((cachedImage.size.height + 16.0f) >= cachedImage.size.width)? NO : YES; | |
cachedImage = [cachedImage scaleImageToWidth:((isLandscapeImage)? frame.size.width : (frame.size.width / 3) )]; | |
[[SDImageCache sharedImageCache] storeImage:cachedImage forKey:imageURL]; | |
} | |
_image = cachedImage; | |
_contentHeight += _image.size.height + 10.0f; | |
} | |
return self; | |
} | |
- (void)drawRect:(CGRect)rect | |
{ | |
[super drawRect:rect]; | |
CGRect imageFrame = CGRectZero; | |
if(_image) | |
{ | |
BOOL isLandscapeImage = ((_image.size.height + 16.0f) >= _image.size.width)? NO : YES; | |
imageFrame.size.width = (isLandscapeImage)? rect.size.width : _image.size.width; | |
imageFrame.size.height = _image.size.height; | |
imageFrame.origin.y = rect.origin.y; | |
imageFrame.origin.x = (isLandscapeImage)? rect.origin.x : (rect.size.width - imageFrame.size.width); | |
//[_image drawInRect:CGRectInset(imageFrame, IMAGE_INSET, IMAGE_INSET)]; | |
UIImageView *imageView = [[UIImageView alloc] initWithImage:_image]; | |
imageView.frame = CGRectInset(imageFrame, ARTICLE_VIEW_IMAGE_INSET, ARTICLE_VIEW_IMAGE_INSET); | |
imageView.autoresizingMask = FLEX_SIZE; | |
imageView.borderColor = WHITE; | |
imageView.borderWidth = ARTICLE_VIEW_IMAGE_INSET; | |
LAYER_DROP_SHADOW(imageView, BLACK, CGSizeMake(0.0f, 4.0f), 0.2f, 2.0f); | |
[self addSubview:imageView]; | |
} | |
CFMutableAttributedStringRef attrStringRef = (__bridge CFMutableAttributedStringRef)_text; | |
CGContextRef context = UIGraphicsGetCurrentContext(); | |
CGContextSetTextMatrix(context, CGAffineTransformIdentity); | |
CGContextTranslateCTM(context, 0, rect.size.height); | |
CGContextScaleCTM(context, 1.0f, -1.0f); | |
CGMutablePathRef path = CGPathCreateMutable(); | |
CGPathAddRect(path, NULL, rect); | |
CGMutablePathRef imageClippingPath = CGPathCreateMutable(); | |
CGPathAddRect(imageClippingPath, NULL, CGRectMake((CGRectGetMaxX(rect) - CGRectGetWidth(imageFrame)), | |
CGRectGetMaxY(rect) - (CGRectGetHeight(imageFrame)), | |
CGRectGetWidth(imageFrame), CGRectGetHeight(imageFrame))); | |
CFStringRef keys[] = { kCTFramePathClippingPathAttributeName }; | |
CFTypeRef values[] = { imageClippingPath }; | |
CFDictionaryRef clippingPathDict = CFDictionaryCreate(NULL, (const void **)&keys, (const void **)&values, (sizeof(keys) / sizeof(keys[0])), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
// An array of clipping paths -- you can use more than one if needed! | |
NSArray *clippingPaths = [NSArray arrayWithObject:(__bridge NSDictionary*)clippingPathDict]; | |
// Create an options dictionary, to pass in to CTFramesetter | |
NSDictionary *optionsDict = [NSDictionary dictionaryWithObject:clippingPaths forKey:(NSString*)kCTFrameClippingPathsAttributeName]; | |
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrStringRef); | |
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, (__bridge CFDictionaryRef)optionsDict); | |
CTFrameDraw(frame, context); | |
CFRelease(frame); | |
CFRelease(framesetter); | |
CFRelease(path); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment