Skip to content

Instantly share code, notes, and snippets.

@klazuka
Created April 26, 2013 21:16
Show Gist options
  • Select an option

  • Save klazuka/5470506 to your computer and use it in GitHub Desktop.

Select an option

Save klazuka/5470506 to your computer and use it in GitHub Desktop.
iOS: basic 2-column text layout
//
// TwoColumnTextArea.h
// TastingRoom
//
// Created by Keith Lazuka on 4/26/13.
// Copyright (c) 2013 Keith Lazuka. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface TwoColumnTextArea : UIView
// the width of each column is equal to 0.5*(self.width - (self.width*self.gutterRatio)),
// with some rounding to keep everything on whole pixel boundaries
@property (nonatomic, readwrite, strong) NSString *text;
@property (nonatomic, readwrite, strong) UIColor *textColor;
@property (nonatomic, readwrite, strong) UIFont *font;
@property (nonatomic, readwrite, assign) float minimumLineHeight; // controls the leading
@property (nonatomic, readwrite, assign) float gutterRatio; // percentage of the view's width devoted to the center text gutter.
@end
//
// TwoColumnTextArea.m
// TastingRoom
//
// Created by Keith Lazuka on 4/26/13.
// Copyright (c) 2013 Keith Lazuka. All rights reserved.
//
#import <CoreText/CoreText.h>
#import "TwoColumnTextArea.h"
@implementation TwoColumnTextArea
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
self.userInteractionEnabled = NO;
self.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
self.textColor = [UIColor blackColor];
self.minimumLineHeight = self.font.pointSize;
self.backgroundColor = [UIColor whiteColor];
self.gutterRatio = 0.05; // 5% gutter by default
}
return self;
}
- (void)drawRect:(CGRect)rect
{
if (!_text || _text.length == 0)
return;
NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:_text];
// style the text
CTFontRef appFont = CTFontCreateWithName((__bridge CFStringRef)[_font fontName], [_font pointSize], NULL);
NSRange entireStringRange = NSMakeRange(0, string.length);
[string addAttribute:(id)kCTFontAttributeName value:(__bridge id)appFont range:entireStringRange];
[string addAttribute:(id)kCTForegroundColorAttributeName value:(__bridge id)self.textColor.CGColor range:entireStringRange];
CTParagraphStyleSetting leading = {
.spec = kCTParagraphStyleSpecifierMinimumLineHeight,
.value = &_minimumLineHeight,
.valueSize = sizeof(_minimumLineHeight),
};
CTParagraphStyleRef paraStyle = CTParagraphStyleCreate(&leading, 1);
[string addAttribute:(id)kCTParagraphStyleAttributeName value:(__bridge id)paraStyle range:entireStringRange];
// derived layout parameters
float gutterWidth = roundf(self.gutterRatio * self.width);
float contentWidth = self.width - gutterWidth;
float leftColumnWidth = floorf(contentWidth/2.0);
float rightColumnWidth = contentWidth - leftColumnWidth;
// layout master
CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString((__bridge CFAttributedStringRef)string);
// left column frame
CGMutablePathRef leftColumnPath = CGPathCreateMutable();
CGPathAddRect(leftColumnPath, NULL, CGRectMake(0, 0, leftColumnWidth, self.bounds.size.height));
CTFrameRef leftFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), leftColumnPath, NULL);
// right column frame
CGMutablePathRef rightColumnPath = CGPathCreateMutable();
CGPathAddRect(rightColumnPath, NULL, CGRectMake(leftColumnWidth + gutterWidth, 0, rightColumnWidth, self.bounds.size.height));
NSInteger rightColumStart = CTFrameGetVisibleStringRange(leftFrame).length;
CTFrameRef rightFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(rightColumStart, 0), rightColumnPath, NULL);
// flip the coordinate system
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetTextMatrix(context, CGAffineTransformIdentity);
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// draw
CTFrameDraw(leftFrame, context);
CTFrameDraw(rightFrame, context);
// cleanup
CFRelease(leftFrame);
CGPathRelease(leftColumnPath);
CFRelease(rightFrame);
CGPathRelease(rightColumnPath);
CFRelease(framesetter);
CFRelease(appFont);
CFRelease(paraStyle);
}
- (void)setText:(NSString *)text
{
if (_text == text)
return;
_text = text;
[self setNeedsDisplay];
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment