Skip to content

Instantly share code, notes, and snippets.

@ahmattox
Last active September 16, 2016 16:36
Show Gist options
  • Save ahmattox/4596476 to your computer and use it in GitHub Desktop.
Save ahmattox/4596476 to your computer and use it in GitHub Desktop.
Text View with Drop Caps UIView subclass which renders text using core text with an enlarged, floating first character.
//
// FTWDropCapTextView.h
//
// Created by Anthony Mattox on 1/22/13.
// Copyright (c) 2013 Friends of The Web. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface FTWDropCapTextView : UIView
@property (nonatomic) UIEdgeInsets capMargin;
@property (nonatomic) UIEdgeInsets contentInset;
@property (nonatomic, strong) NSDictionary *bodyAttributes;
@property (nonatomic, strong) NSDictionary *capAttributes;
@property (nonatomic, strong) NSString *text;
- (UIFont *)capFont;
- (UIFont *)bodyFont;
- (void)setCapFont:(UIFont *)capFont;
- (void)setBodyFont:(UIFont *)bodyFont;
@end
//
// FTWDropCapTextView.m
//
// Created by Anthony Mattox on 1/22/13.
// Copyright (c) 2013 Friends of The Web. All rights reserved.
//
#import "FTWDropCapTextView.h"
#import <CoreText/CoreText.h>
@interface FTWDropCapTextView ()
@property (nonatomic, strong) NSRegularExpression *validCapRegex;
- (void) setupProperties;
- (void) drawText;
- (void) drawTextWithDropCap;
@end
@implementation FTWDropCapTextView
#pragma mark - Initializing
- (id) initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setupProperties];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self setupProperties];
}
return self;
}
- (void) setupProperties {
self.contentMode = UIViewContentModeRedraw;
_contentInset = UIEdgeInsetsMake(0, 0, 0, 0);
_capMargin = UIEdgeInsetsMake(0, 0, 0, 10);
self.bodyAttributes = @{NSFontAttributeName : [UIFont systemFontOfSize:12]};
self.capAttributes = @{NSFontAttributeName : [UIFont boldSystemFontOfSize:50]};
self.validCapRegex = [[NSRegularExpression alloc] initWithPattern:@"^\\w" options:NSRegularExpressionCaseInsensitive error:nil];
}
#pragma mark - Properties
- (void) setText:(NSString *)text {
_text = text;
[self setNeedsDisplay];
}
- (UIFont *)capFont {
return [self.capAttributes objectForKey:NSFontAttributeName];
}
- (UIFont *)bodyFont {
return [self.bodyAttributes objectForKey:NSFontAttributeName];
}
- (void)setCapFont:(UIFont *)capFont {
NSMutableDictionary *dictionary = [self.capAttributes mutableCopy];
dictionary[NSFontAttributeName] = capFont;
self.capAttributes = [NSDictionary dictionaryWithDictionary:dictionary];
}
- (void)setBodyFont:(UIFont *)bodyFont {
NSMutableDictionary *dictionary = [self.bodyAttributes mutableCopy];
dictionary[NSFontAttributeName] = bodyFont;
self.bodyAttributes = [NSDictionary dictionaryWithDictionary:dictionary];
}
#pragma mark - Rendering
- (void)drawRect:(CGRect)rect {
if (self.text && ![self.text isEqualToString:@""]) {
if (self.text.length>2) {
if ([self.validCapRegex numberOfMatchesInString:self.text options:NSMatchingReportCompletion range:NSMakeRange(0, 1)]) {
[self drawTextWithDropCap];
} else {
[self drawText];
}
} else {
[self drawText];
}
}
}
- (void) drawTextWithDropCap {
// Create attributed strings
NSAttributedString *bodyText = [[NSAttributedString alloc] initWithString:[self.text substringWithRange:NSMakeRange(1, self.text.length -1)] attributes:_bodyAttributes];
NSAttributedString *capText = [[NSAttributedString alloc] initWithString:[[self.text substringWithRange:NSMakeRange(0, 1)] uppercaseString] attributes:_capAttributes];
CGSize capSize = [capText size];
CGRect capFrame = CGRectMake(_capMargin.left, _capMargin.top, capSize.width, capSize.height);
CGRect capSpace = CGRectMake(0, 0, CGRectGetMaxX(capFrame) + _capMargin.left + _capMargin.right, CGRectGetMaxY(capFrame) + _capMargin.top + _capMargin.bottom);
// Set up graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Create type frames
CGMutablePathRef bodyPath = CGPathCreateMutable();
CGAffineTransform transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1), 0, -self.bounds.size.height);
CGPathMoveToPoint(bodyPath, &transform, CGRectGetMaxX(capSpace) + _contentInset.left, _contentInset.top);
CGPathAddLineToPoint(bodyPath, &transform, self.bounds.size.width - _contentInset.right, _contentInset.top);
CGPathAddLineToPoint(bodyPath, &transform, self.bounds.size.width - _contentInset.right, self.bounds.size.height - _contentInset.bottom);
CGPathAddLineToPoint(bodyPath, &transform, _contentInset.left, self.bounds.size.height - _contentInset.bottom);
CGPathAddLineToPoint(bodyPath, &transform, _contentInset.left, CGRectGetMaxY(capSpace) + _contentInset.top);
CGPathAddLineToPoint(bodyPath, &transform, CGRectGetMaxX(capSpace) + _contentInset.left, CGRectGetMaxY(capSpace) + _contentInset.top);
CGPathCloseSubpath(bodyPath);
CGMutablePathRef capPath = CGPathCreateMutable();
CGPathAddRect(capPath, &transform, CGRectMake(_contentInset.left + _capMargin.left, _contentInset.top + _capMargin.top, capSize.width+10, capSize.height+10));
// Draw text
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) bodyText);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), bodyPath, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, context);
CFRelease(frame);
framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)capText);
frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), capPath, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, context);
CFRelease(frame);
}
- (void) drawText {
// Create attributed string
NSAttributedString *bodyText = [[NSAttributedString alloc] initWithString:self.text attributes:_bodyAttributes];
// Set up graphics context
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// Create type frame
CGMutablePathRef bodyPath = CGPathCreateMutable();
CGAffineTransform transform = CGAffineTransformTranslate(CGAffineTransformMakeScale(1, -1), 0, -self.bounds.size.height);
CGPathAddRect(bodyPath, &transform, CGRectMake(_contentInset.left, _contentInset.top, self.bounds.size.width - _contentInset.left - _contentInset.right, self.bounds.size.height - _contentInset.top - _contentInset.bottom));
// Draw text
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef) bodyText);
CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), bodyPath, NULL);
CFRelease(framesetter);
CTFrameDraw(frame, context);
CFRelease(frame);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment