Created
April 26, 2013 21:16
-
-
Save klazuka/5470506 to your computer and use it in GitHub Desktop.
iOS: basic 2-column text layout
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
| // | |
| // 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 |
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
| // | |
| // 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