Last active
September 16, 2016 16:36
-
-
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.
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
// | |
// 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 |
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
// | |
// 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